You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@netbeans.apache.org by GitBox <gi...@apache.org> on 2018/09/27 15:37:44 UTC

[GitHub] geertjanw closed pull request #909: Debugging of Graal languages as a future replacement of Nashorn.

geertjanw closed pull request #909: Debugging of Graal languages as a future replacement of Nashorn.
URL: https://github.com/apache/incubator-netbeans/pull/909
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/java/debugger.jpda.truffle/build.xml b/java/debugger.jpda.truffle/build.xml
new file mode 100644
index 0000000000..d67c8e42f0
--- /dev/null
+++ b/java/debugger.jpda.truffle/build.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<project basedir="." default="netbeans" name="java/debugger.jpda.truffle">
+    <import file="../../nbbuild/templates/projectized.xml"/>
+
+    <target name="build-init" depends="projectized.build-init">
+        <property name="truffle-backend.cp" value="${module.classpath}:${truffle-backend.cp.extra}"/>
+    </target>
+
+    <target name="compile-truffle-backend" depends="init">
+        <mkdir dir="build/truffle-backend-classes"/>
+        <depend srcdir="truffle-backend" destdir="build/truffle-backend-classes" cache="build/depcache-truffle-backend">
+            <classpath>
+                <path path="${truffle-backend.cp}"/>
+            </classpath>
+        </depend>
+        <javac srcdir="truffle-backend" destdir="build/truffle-backend-classes" deprecation="${build.compiler.deprecation}" debug="${build.compiler.debug}" source="1.8" target="1.8" includeantruntime="false">
+            <classpath>
+                <path path="${truffle-backend.cp}"/>
+            </classpath>
+            <compilerarg line="${javac.compilerargs}"/>
+        </javac>
+        <copy todir="build/truffle-backend-classes">
+            <fileset dir="truffle-backend" excludes="${jar-excludes}"/>
+        </copy>
+        <mkdir dir="build/classes/org/netbeans/modules/debugger/jpda/truffle/resources"/>
+        <jar jarfile="build/classes/org/netbeans/modules/debugger/jpda/truffle/resources/JPDATruffleBackend.jar" compress="false">
+            <fileset dir="build/truffle-backend-classes"/>
+        </jar>
+    </target>
+
+    <target name="compile" depends="compile-truffle-backend,projectized-common.compile"/>
+</project>
diff --git a/java/debugger.jpda.truffle/external/antlr4-runtime-4.7-license.txt b/java/debugger.jpda.truffle/external/antlr4-runtime-4.7-license.txt
new file mode 100644
index 0000000000..a7603212bb
--- /dev/null
+++ b/java/debugger.jpda.truffle/external/antlr4-runtime-4.7-license.txt
@@ -0,0 +1,35 @@
+Name: Antlr
+Description: ANother Tool for Language Recognition, is a language tool that provides a framework for constructing recognizers, interpreters, compilers, and translators from grammatical descriptions.
+Version: 4.7
+License: BSD-antlr-runtime4
+Origin: Antlr
+URL: http://www.antlr.org
+
+Use of Antlr version 4.5 is governed by the terms of the license below:
+
+ [The "BSD license"]
+Copyright (c) 2015 Terence Parr, Sam Harwell
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ 1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+ 3. The name of the author may not be used to endorse or promote products
+    derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/java/debugger.jpda.truffle/external/binaries-list b/java/debugger.jpda.truffle/external/binaries-list
new file mode 100644
index 0000000000..019c567522
--- /dev/null
+++ b/java/debugger.jpda.truffle/external/binaries-list
@@ -0,0 +1,20 @@
+# 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.
+07B63F4902CA51B574EA892D5D5E8088B8FBF97A org.graalvm:graal-sdk:1.0.0-rc6
+F4EE58B11D2CD123BA6C8F5B87B1861D8DD31841 com.oracle.truffle:truffle-api:1.0.0-rc6
+93A352D21C1D95061BDFE5C86A91F946F3A3E605 com.oracle.truffle:truffle-sl:1.0.0-rc6
+30B13B7EFC55B7FEEA667691509CF59902375001 org.antlr:antlr4-runtime:4.7
diff --git a/java/debugger.jpda.truffle/external/truffle-1.0.0-license.txt b/java/debugger.jpda.truffle/external/truffle-1.0.0-license.txt
new file mode 100644
index 0000000000..77e34b0a07
--- /dev/null
+++ b/java/debugger.jpda.truffle/external/truffle-1.0.0-license.txt
@@ -0,0 +1,26 @@
+Name: Graal SDK and Truffle API
+Description: Graal SDK and Truffle API
+License: UPL
+Origin: https://github.com/oracle/graal
+Version: 1.0
+Files: graal-sdk-1.0.0-rc6.jar, truffle-api-1.0.0-rc6.jar, truffle-sl-1.0.0-rc6.jar
+OSR: not needed
+
+Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+
+The Universal Permissive License (UPL), Version 1.0
+
+Subject to the condition set forth below, permission is hereby granted to any person obtaining a copy of this software, associated documentation and/or data (collectively the "Software"), free of charge and under any and all copyright rights in the Software, and any and all patent rights owned or freely licensable by each licensor hereunder covering either (i) the unmodified Software as contributed to or provided by such licensor, or (ii) the Larger Works (as defined below), to deal in both
+
+(a) the Software, and
+
+(b) any piece of software and/or hardware listed in the lrgrwrks.txt file if one is included with the Software each a "Larger Work" to which the Software is contributed by such licensors),
+
+without restriction, including without limitation the rights to copy, create derivative works of, display, perform, and distribute the Software and make, use, sell, offer for sale, import, export, have made, and have sold the Software and the Larger Work(s), and to sublicense the foregoing rights on either these or other terms.
+
+This license is subject to the following condition:
+
+The above copyright notice and either this complete permission notice or at a minimum a reference to the UPL must be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
diff --git a/java/debugger.jpda.truffle/manifest.mf b/java/debugger.jpda.truffle/manifest.mf
new file mode 100644
index 0000000000..24240984ee
--- /dev/null
+++ b/java/debugger.jpda.truffle/manifest.mf
@@ -0,0 +1,10 @@
+Manifest-Version: 1.0
+AutoUpdate-Show-In-Client: true
+OpenIDE-Module: org.netbeans.modules.debugger.jpda.truffle/1
+OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/debugger/jpda/truffle/Bundle.properties
+OpenIDE-Module-Layer: org/netbeans/modules/debugger/jpda/truffle/layer.xml
+OpenIDE-Module-Specification-Version: 1.0
+OpenIDE-Module-Provides: org.netbeans.modules.debugger.jpda.truffle
+OpenIDE-Module-Requires: org.netbeans.api.debugger.jpda.JPDADebuggerEngineImpl,
+  org.netbeans.spi.debugger.ui
+OpenIDE-Module-Package-Dependencies: com.sun.jdi[VirtualMachineManager]
diff --git a/java/debugger.jpda.truffle/nbproject/project.properties b/java/debugger.jpda.truffle/nbproject/project.properties
new file mode 100644
index 0000000000..243b1e16f9
--- /dev/null
+++ b/java/debugger.jpda.truffle/nbproject/project.properties
@@ -0,0 +1,27 @@
+# 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.
+
+javac.compilerargs=-Xlint:unchecked
+javac.source=1.8
+javadoc.arch=${basedir}/arch.xml
+nbm.module.author=Martin Entlicher
+requires.nb.javac=true
+truffle-backend.cp.extra=external/graal-sdk-1.0.0-rc6.jar:external/truffle-api-1.0.0-rc6.jar
+truffle.sl=external/antlr4-runtime-4.7.jar:external/truffle-sl-1.0.0-rc6.jar
+cp.extra=${tools.jar}:${truffle-backend.cp.extra}:${truffle.sl}
+test-unit-sys-prop.test.dir.src=${basedir}/test/unit/src/
+test-unit-sys-prop.netbeans.user=${basedir}/work/nb_user_dir
diff --git a/java/debugger.jpda.truffle/nbproject/project.xml b/java/debugger.jpda.truffle/nbproject/project.xml
new file mode 100644
index 0000000000..6f80a75a01
--- /dev/null
+++ b/java/debugger.jpda.truffle/nbproject/project.xml
@@ -0,0 +1,270 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<project xmlns="http://www.netbeans.org/ns/project/1">
+    <type>org.netbeans.modules.apisupport.project</type>
+    <configuration>
+        <data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
+            <code-name-base>org.netbeans.modules.debugger.jpda.truffle</code-name-base>
+            <module-dependencies>
+                <dependency>
+                    <code-name-base>org.netbeans.api.debugger</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.51</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.api.debugger.jpda</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>2</release-version>
+                        <specification-version>3.5</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.api.java.classpath</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.52</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.debugger.jpda</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>2</release-version>
+                        <specification-version>1.98</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.debugger.jpda.ui</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.55</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.extexecution.base</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>2</release-version>
+                        <specification-version>1.4</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.java.kit</code-name-base>
+                    <run-dependency>
+                        <specification-version>1.0</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.java.platform</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.40</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.java.project</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.66</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.javascript2.debug</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.10</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.options.api</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.46</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.projectapi</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.63</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.spi.debugger.ui</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>2.50</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.spi.viewmodel</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>2</release-version>
+                        <specification-version>1.49</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.awt</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>7.71</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.dialogs</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>7.43</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.filesystems</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>9.4</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.io</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>1.47</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.loaders</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>7.70</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.modules</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>7.48</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.nodes</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>7.41</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.text</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>6.65</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.util</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>9.3</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.util.lookup</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>8.30</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.util.ui</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>9.3</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.windows</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>6.73</specification-version>
+                    </run-dependency>
+                </dependency>
+            </module-dependencies>
+            <test-dependencies>
+                <test-type>
+                    <name>unit</name>
+                    <test-dependency>
+                        <code-name-base>org.netbeans.modules.debugger.jpda</code-name-base>
+                        <compile-dependency/>
+                        <test/>
+                    </test-dependency>
+                    <test-dependency>
+                        <code-name-base>org.netbeans.modules.nbjunit</code-name-base>
+                        <compile-dependency/>
+                    </test-dependency>
+                </test-type>
+            </test-dependencies>
+            <public-packages/>
+            <extra-compilation-unit>
+                <package-root>truffle-backend</package-root>
+                <classpath>${module.classpath}:${truffle-backend.cp.extra}</classpath>
+                <built-to>build/bridge-classes</built-to>
+            </extra-compilation-unit>
+        </data>
+    </configuration>
+</project>
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/Bundle.properties b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/Bundle.properties
new file mode 100644
index 0000000000..e3d3f4dd97
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/Bundle.properties
@@ -0,0 +1,22 @@
+# 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.
+
+# module description
+OpenIDE-Module-Name=GraalVM Debugging Support
+OpenIDE-Module-Display-Category=Java SE
+OpenIDE-Module-Short-Description=Supports Debugging of Scripts on GraalVM
+OpenIDE-Module-Long-Description=Supports debugging of scripts interpreted by Truffle in GraalVM.
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
new file mode 100644
index 0000000000..a6b2e81f2e
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/DebugManagerHandler.java
@@ -0,0 +1,289 @@
+/*
+ * 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.BooleanValue;
+import com.sun.jdi.ClassNotLoadedException;
+import com.sun.jdi.ClassObjectReference;
+import com.sun.jdi.ClassType;
+import com.sun.jdi.Field;
+import com.sun.jdi.IncompatibleThreadStateException;
+import com.sun.jdi.InvalidTypeException;
+import com.sun.jdi.InvocationException;
+import com.sun.jdi.Method;
+import com.sun.jdi.ObjectReference;
+import com.sun.jdi.ThreadReference;
+import com.sun.jdi.Value;
+import com.sun.jdi.VirtualMachine;
+
+import java.beans.PropertyVetoException;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.Lock;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.netbeans.api.debugger.jpda.JPDAClassType;
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.JPDAThread;
+import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
+import org.netbeans.modules.debugger.jpda.expr.InvocationExceptionTranslated;
+import org.netbeans.modules.debugger.jpda.jdi.ClassNotPreparedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.ClassObjectReferenceWrapper;
+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.ReferenceTypeWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.UnsupportedOperationExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.VMDisconnectedExceptionWrapper;
+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.javascript2.debug.breakpoints.JSLineBreakpoint;
+import org.openide.util.Exceptions;
+
+/**
+ * Handles remote JPDATruffleDebugManager.
+ */
+final class DebugManagerHandler {
+    
+    private static final Logger LOG = Logger.getLogger(DebugManagerHandler.class.getName());
+    
+    private static final String ACCESSOR_START_ACCESS_LOOP = "startAccessLoop"; // NOI18N
+    private static final String ACCESSOR_LOOP_RUNNING_FIELD = "accessLoopRunning";  // NOI18N
+    private static final String ACCESSOR_SET_UP_DEBUG_MANAGER_FOR = "setUpDebugManagerFor"; // NOI18N
+    private static final String ACCESSOR_SET_UP_DEBUG_MANAGER_FOR_SGN =
+            "(L"+Object.class.getName().replace('.', '/')+";Z)"+                // NOI18N
+            "Lorg/netbeans/modules/debugger/jpda/backend/truffle/JPDATruffleDebugManager;"; // NOI18N
+    
+    private static final Map<JPDADebugger, Boolean> dbgStepInto = Collections.synchronizedMap(new WeakHashMap<JPDADebugger, Boolean>());
+
+    private final JPDADebugger debugger;
+    private final AtomicBoolean inited = new AtomicBoolean(false);
+    private ClassType accessorClass;
+    private JPDAClassType accessorJPDAClass;
+    private final Object accessorClassLock = new Object();
+    //private ObjectReference debugManager;
+    private final TruffleBreakpointsHandler breakpointsHandler;
+    
+    public DebugManagerHandler(JPDADebugger debugger) {
+        this.debugger = debugger;
+        this.breakpointsHandler = new TruffleBreakpointsHandler(debugger);
+    }
+    
+    static void execStepInto(JPDADebugger debugger, boolean doStepInto) {
+        if (doStepInto) {
+            dbgStepInto.put(debugger, doStepInto);
+        } else {
+            dbgStepInto.remove(debugger);
+        }
+    }
+    
+    private boolean isStepInto() {
+        Boolean stepInto = dbgStepInto.get(debugger);
+        return stepInto != null && stepInto;
+    }
+    
+    void newPolyglotEngineInstance(ObjectReference engine, JPDAThreadImpl thread) {
+        LOG.log(Level.FINE, "Engine created breakpoint hit: engine = {0} in thread = {1}", new Object[] { engine, thread.getThreadReference()});
+        if (inited.compareAndSet(false, true)) {
+            initDebuggerRemoteService(thread);
+        }
+        if (accessorClass == null) {
+            // No accessor
+            return ;
+        }
+        InvocationExceptionTranslated iextr = null;
+        try {
+            // Create an instance of JPDATruffleDebugManager in the backend
+            // and submit breakpoints:
+            thread.notifyMethodInvoking();
+            VirtualMachine vm = ((JPDADebuggerImpl) debugger).getVirtualMachine();
+            if (vm == null) {
+                return ;
+            }
+            BooleanValue doStepInto = vm.mirrorOf(isStepInto());
+            Method debugManagerMethod = ClassTypeWrapper.concreteMethodByName(
+                    accessorClass,
+                    ACCESSOR_SET_UP_DEBUG_MANAGER_FOR,
+                    ACCESSOR_SET_UP_DEBUG_MANAGER_FOR_SGN);
+            ThreadReference tr = thread.getThreadReference();
+            List<Value> dmArgs = Arrays.asList(engine, doStepInto);
+            LOG.log(Level.FINE, "Setting engine and step into = {0}", isStepInto());
+            Object ret = ClassTypeWrapper.invokeMethod(accessorClass, tr, debugManagerMethod, dmArgs, ObjectReference.INVOKE_SINGLE_THREADED);
+            if (ret instanceof ObjectReference) {   // Can be null when an existing debug manager is reused.
+                //debugManager = (ObjectReference) ret;
+                breakpointsHandler.submitBreakpoints(accessorClass, (ObjectReference) ret, thread);
+            }
+        } catch (VMDisconnectedExceptionWrapper vmd) {
+        } catch (InvocationException iex) {
+            iextr = new InvocationExceptionTranslated(iex, thread.getDebugger());
+            Exceptions.printStackTrace(iex);
+        } catch (Exception ex) {
+            Exceptions.printStackTrace(ex);
+        } finally {
+            thread.notifyMethodInvokeDone();
+        }
+        if (iextr != null) {
+            iextr.preload(thread);
+            Exceptions.printStackTrace(iextr);
+        }
+    }
+
+    private void initDebuggerRemoteService(JPDAThread thread) {
+        if (LOG.isLoggable(Level.FINE)) {
+            LOG.log(Level.FINE, "initDebuggerRemoteService({0})", thread);
+        }
+        JPDAThreadImpl t = (JPDAThreadImpl) thread;
+        Lock writeLock = t.accessLock.writeLock();
+        writeLock.lock();
+        try {
+            ClassObjectReference cor = null;
+            try {
+                cor = RemoteServices.uploadBasicClasses(t, BASIC_CLASS_NAME);
+            } catch (PropertyVetoException |
+                     InvalidTypeException |
+                     ClassNotLoadedException |
+                     IncompatibleThreadStateException |
+                     IOException pvex) {
+                Exceptions.printStackTrace(pvex);
+            } catch (InvocationException ex) {
+                Exceptions.printStackTrace(ex);
+                final InvocationExceptionTranslated iextr = new InvocationExceptionTranslated(ex, t.getDebugger());
+                iextr.preload(t);
+                Exceptions.printStackTrace(iextr);
+            }
+            if (LOG.isLoggable(Level.FINE)) {
+                LOG.log(Level.FINE, "Uploaded class = {0}", cor);
+            }
+            if (cor == null) {
+                return ;
+            }
+            ThreadReference tr = t.getThreadReference();
+            
+            ClassType serviceClass = (ClassType) ClassObjectReferenceWrapper.reflectedType(cor);//RemoteServices.getClass(vm, "org.netbeans.modules.debugger.jpda.visual.remote.RemoteService");
+
+            InvocationExceptionTranslated iextr = null;
+            Method startMethod = ClassTypeWrapper.concreteMethodByName(serviceClass, ACCESSOR_START_ACCESS_LOOP, "()Ljava/lang/Thread;");
+            if (startMethod == null) {
+                LOG.log(Level.WARNING, "Could not start the access loop of "+serviceClass+", no "+ACCESSOR_START_ACCESS_LOOP+" method.");
+                return ;
+            }
+            try {
+                t.notifyMethodInvoking();
+                Value ret = ClassTypeWrapper.invokeMethod(serviceClass, tr, startMethod, Collections.emptyList(), ObjectReference.INVOKE_SINGLE_THREADED);
+                if (ret instanceof ThreadReference) {
+                    RemoteServices.setAccessLoopStarted(t.getDebugger(), (ThreadReference) ret);
+                } else {
+                    LOG.log(Level.WARNING, "Could not start the access loop of "+serviceClass);
+                    return ;
+                }
+                TruffleAccess.assureBPSet(debugger, serviceClass);
+                JPDAClassType serviceJPDAClass = ((JPDADebuggerImpl) debugger).getClassType(serviceClass);
+                synchronized (accessorClassLock) {
+                    accessorClass = serviceClass;
+                    accessorJPDAClass = serviceJPDAClass;
+                }
+            } catch (InvocationException iex) {
+                iextr = new InvocationExceptionTranslated(iex, t.getDebugger());
+                Exceptions.printStackTrace(iex);
+            } catch (ClassNotLoadedException |
+                     IncompatibleThreadStateException |
+                     InvalidTypeException |
+                     PropertyVetoException |
+                     InternalExceptionWrapper |
+                     ObjectCollectedExceptionWrapper ex) {
+                Exceptions.printStackTrace(ex);
+            } finally {
+                t.notifyMethodInvokeDone();
+                ObjectReferenceWrapper.enableCollection(cor);
+            }
+            if (iextr != null) {
+                iextr.preload(t);
+                Exceptions.printStackTrace(iextr);
+            }
+        } catch (InternalExceptionWrapper iex) {
+        } catch (ClassNotPreparedExceptionWrapper cnpex) {
+            Exceptions.printStackTrace(cnpex);
+        } catch (ObjectCollectedExceptionWrapper collex) {
+        } catch (UnsupportedOperationExceptionWrapper uex) {
+            LOG.log(Level.INFO, uex.getLocalizedMessage(), uex);
+        } catch (VMDisconnectedExceptionWrapper vmd) {
+        } finally {
+            writeLock.unlock();
+        }
+        if (LOG.isLoggable(Level.FINE)) {
+            try {
+                LOG.fine("The JPDATruffleAccessor is there: "+
+                            RemoteServices.getClass(t.getThreadReference().virtualMachine(),
+                         BASIC_CLASS_NAME));
+            } catch (Exception ex) {
+                LOG.log(Level.FINE, "", ex);
+            }
+        }
+    }
+    
+    ClassType getAccessorClass() {
+        synchronized (accessorClassLock) {
+            return accessorClass;
+        }
+    }
+    
+    JPDAClassType getAccessorJPDAClass() {
+        synchronized (accessorClassLock) {
+            return accessorJPDAClass;
+        }
+    }
+    
+    void destroy() {
+        breakpointsHandler.destroy();
+        if (accessorClass == null) {
+            return ;
+        }
+        try {
+            Field accessLoopRunning = ReferenceTypeWrapper.fieldByName(accessorClass, ACCESSOR_LOOP_RUNNING_FIELD);
+            if (accessLoopRunning != null) {
+                ClassTypeWrapper.setValue(accessorClass, accessLoopRunning,
+                                          VirtualMachineWrapper.mirrorOf(accessorClass.virtualMachine(), false));
+                RemoteServices.interruptServiceAccessThread(debugger);
+            }
+        } catch (VMDisconnectedExceptionWrapper vdex) {
+            // Ignore
+        } catch (Exception ex) {
+            Exceptions.printStackTrace(ex);
+        }
+    }
+    
+    void breakpointAdded(JSLineBreakpoint jsLineBreakpoint) {
+        breakpointsHandler.breakpointAdded(jsLineBreakpoint);
+    }
+
+    void breakpointRemoved(JSLineBreakpoint jsLineBreakpoint) {
+        breakpointsHandler.breakpointRemoved(jsLineBreakpoint);
+    }
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/FirstSourceURLProvider.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/FirstSourceURLProvider.java
new file mode 100644
index 0000000000..2340344fe5
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/FirstSourceURLProvider.java
@@ -0,0 +1,108 @@
+/*
+ * 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 java.beans.PropertyChangeListener;
+import java.net.URL;
+import org.netbeans.api.debugger.jpda.JPDAClassType;
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.JPDAThread;
+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.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.jpda.SourcePathProvider;
+
+/**
+ * Recognizes suspended location in a GraalVM guest language script.
+ */
+@SourcePathProvider.Registration(path = "netbeans-JPDASession")
+public class FirstSourceURLProvider extends SourcePathProvider {
+    
+    private static final String[] NO_SOURCE_ROOTS = new String[]{};
+    
+    private static final String TRUFFLE_ACCESSOR_CLASS_NAME =
+            "org.netbeans.modules.debugger.jpda.backend.truffle.JPDATruffleAccessor"; // NOI18N
+    private static final String TRUFFLE_ACCESSOR_PATH =
+            "org/netbeans/modules/debugger/jpda/backend/truffle/JPDATruffleAccessor"; // NOI18N
+    
+    private final JPDADebugger debugger;
+    
+    public FirstSourceURLProvider(ContextProvider contextProvider) {
+        debugger = contextProvider.lookupFirst(null, JPDADebugger.class);
+    }
+
+    @Override
+    public String getURL(String relativePath, boolean global) {
+        if (TRUFFLE_ACCESSOR_PATH.equals(relativePath)) {
+            JPDAThread currentThread = debugger.getCurrentThread();
+            CurrentPCInfo currentPCInfo = TruffleAccess.getCurrentPCInfo(currentThread);
+            if (currentPCInfo != null) {
+                return currentPCInfo.getSourcePosition().getSource().getUrl().toExternalForm();
+            }
+        }
+        return null;
+    }
+    
+    public String getURL(JPDAClassType clazz, String stratum) {
+        if (TRUFFLE_ACCESSOR_CLASS_NAME.equals(clazz.getName())) {
+            JPDAThread currentThread = debugger.getCurrentThread();
+            CurrentPCInfo currentPCInfo = TruffleAccess.getCurrentPCInfo(currentThread);
+            if (currentPCInfo != null) {
+                Source source = currentPCInfo.getSourcePosition().getSource();
+                if (source != null) {
+                    URL url = source.getUrl();
+                    if (url != null) {
+                        return url.toExternalForm();
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public String getRelativePath(String url, char directorySeparator, boolean includeExtension) {
+        return null;
+    }
+
+    @Override
+    public String[] getSourceRoots() {
+        return NO_SOURCE_ROOTS;
+    }
+
+    @Override
+    public void setSourceRoots(String[] sourceRoots) {
+    }
+
+    @Override
+    public String[] getOriginalSourceRoots() {
+        return NO_SOURCE_ROOTS;
+    }
+
+    @Override
+    public void addPropertyChangeListener(PropertyChangeListener l) {
+    }
+
+    @Override
+    public void removePropertyChangeListener(PropertyChangeListener l) {
+    }
+    
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/MIMETypes.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/MIMETypes.java
new file mode 100644
index 0000000000..611fbc48d6
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/MIMETypes.java
@@ -0,0 +1,197 @@
+/*
+ * 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 java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.netbeans.api.extexecution.base.ProcessBuilder;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.platform.JavaPlatform;
+import org.netbeans.api.java.platform.JavaPlatformManager;
+import org.netbeans.api.java.platform.Specification;
+import org.netbeans.api.java.project.JavaProjectConstants;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectUtils;
+import org.netbeans.api.project.SourceGroup;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+
+/**
+ * Get the MIME types of languages installed in the Truffle/GraalVM platform.
+ */
+public final class MIMETypes {
+
+    private static final Logger LOG = Logger.getLogger(MIMETypes.class.getName());
+    
+    public static final String PROP_MIME_TYPES = "MIME types";                  // NOI18N
+    
+    private static final String MIME_TYPES_MAIN = "org.netbeans.modules.debugger.jpda.backend.truffle.GetMIMETypes";    // NOI18N
+    private static final MIMETypes INSTANCE = new MIMETypes();
+    private static String TEMP_TRUFFLE_JAR;
+
+    private final Map<JavaPlatform, Set<String>> platformMIMETypes = new WeakHashMap<>();
+    private Set<String> allPlatformsMIMETypes;
+    private PropertyChangeListener allPlatformsListener;
+    
+    private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
+    
+    private MIMETypes() {
+    }
+    
+    public static MIMETypes getDefault() {
+        return INSTANCE;
+    }
+    
+    public Set<String> get(Project prj) {
+        JavaPlatform jp = getProjectPlatform(prj);
+        if (jp == null) {
+            return Collections.emptySet();
+        }
+        return get(jp);
+    }
+    
+    private synchronized Set<String> get(JavaPlatform jp) {
+        Set<String> mTypes = platformMIMETypes.get(jp);
+        if (mTypes == null) {
+            FileObject graalvm = jp.findTool("polyglot");                       // NOI18N
+            FileObject java = jp.findTool("java");                              // NOI18N
+            if (graalvm != null && java != null) {
+                File javaFile = FileUtil.toFile(java);
+                if (javaFile != null) {
+                    ProcessBuilder pb = ProcessBuilder.getLocal();
+                    pb.setExecutable(javaFile.getAbsolutePath());
+                    try {
+                        pb.setArguments(Arrays.asList("-cp", getTruffleJarPath(), MIME_TYPES_MAIN));   // NOI18N
+                        Process proc = pb.call();
+                        try (BufferedReader r = new BufferedReader(new InputStreamReader(proc.getInputStream()))) {
+                            mTypes = new HashSet<>();
+                            String line;
+                            while ((line = r.readLine()) != null) {
+                                mTypes.add(line);
+                            }
+                        }
+                        try (BufferedReader r = new BufferedReader(new InputStreamReader(proc.getErrorStream()))) {
+                            String line;
+                            while ((line = r.readLine()) != null) {
+                                LOG.info("Error from "+javaFile+" : "+line);
+                            }
+                        }
+                        LOG.log(Level.FINE, "MIME types of {0} are: {1}", new Object[]{jp, mTypes});
+                    } catch (IOException ioex) {
+                        LOG.log(Level.CONFIG, "", ioex);
+                    }
+                }
+            }
+            if (mTypes == null) {
+                mTypes = Collections.emptySet();
+            }
+            platformMIMETypes.put(jp, mTypes);
+        }
+        return mTypes;
+    }
+    
+    private static synchronized String getTruffleJarPath() throws IOException {
+        if (TEMP_TRUFFLE_JAR == null) {
+            File truffleJarFile = File.createTempFile("TmpTruffleBcknd", ".jar");   // NOI18N
+            truffleJarFile.deleteOnExit();
+            FileUtil.copy(RemoteServices.openRemoteClasses(), new FileOutputStream(truffleJarFile));
+            TEMP_TRUFFLE_JAR = truffleJarFile.getAbsolutePath();
+        }
+        return TEMP_TRUFFLE_JAR;
+    }
+    
+    /**
+     * Get MIME types based on registered Java platforms.
+     * The call returns either a cached set, or queries the platforms.
+     * 
+     * @return a set of MIME types.
+     */
+    public synchronized Set<String> get() {
+        if (allPlatformsMIMETypes != null) {
+            return allPlatformsMIMETypes;
+        }
+        JavaPlatformManager pm = JavaPlatformManager.getDefault();
+        if (allPlatformsListener == null) {
+            allPlatformsListener = new PropertyChangeListener() {
+                @Override public void propertyChange(PropertyChangeEvent evt) {
+                    synchronized (MIMETypes.this) {
+                        allPlatformsMIMETypes = null;
+                    }
+                    pcs.firePropertyChange(PROP_MIME_TYPES, null, null);
+                }
+            };
+            pm.addPropertyChangeListener(allPlatformsListener);
+        }
+        JavaPlatform[] installedPlatforms = pm.getPlatforms(null, new Specification ("j2se", null));   //NOI18N
+        Set<String> mTypes = new HashSet<>();
+        for (int i = 0; i < installedPlatforms.length; i++) {
+            mTypes.addAll(get(installedPlatforms[i]));
+        }
+        allPlatformsMIMETypes = mTypes;
+        return mTypes;
+    }
+    
+    /**
+     * Get cached MIME types based on registered Java platforms.
+     * @return a cached set, or <code>null</code>.
+     */
+    public synchronized Set<String> getCached() {
+        return allPlatformsMIMETypes;
+    }
+    
+    private static JavaPlatform getProjectPlatform(Project prj) {
+        SourceGroup[] sourceGroups = ProjectUtils.getSources(prj).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
+        ClassPath bootClassPath = ClassPath.getClassPath(sourceGroups[0].getRootFolder(), ClassPath.BOOT);
+        FileObject[] prjBootRoots = bootClassPath.getRoots();
+        JavaPlatformManager pm = JavaPlatformManager.getDefault();
+        JavaPlatform[] installedPlatforms = pm.getPlatforms(null, new Specification ("j2se", null));   //NOI18N
+        for (int i = 0; i < installedPlatforms.length; i++) {
+            ClassPath bootstrapLibraries = installedPlatforms[i].getBootstrapLibraries();
+            if (Arrays.equals(prjBootRoots, bootstrapLibraries.getRoots())) {
+                return installedPlatforms[i];
+            }
+        }
+        return null;
+    }
+    
+    public void addPropertyChangeListener(PropertyChangeListener l) {
+        pcs.addPropertyChangeListener(l);
+    }
+    
+    public void removePropertyChangeListener(PropertyChangeListener l) {
+        pcs.removePropertyChangeListener(l);
+    }
+    
+}
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
new file mode 100644
index 0000000000..5ad1d57f6b
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/RemoteServices.java
@@ -0,0 +1,774 @@
+/*
+ * 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.ArrayReference;
+import com.sun.jdi.ArrayType;
+import com.sun.jdi.BooleanValue;
+import com.sun.jdi.ByteValue;
+import com.sun.jdi.ClassNotLoadedException;
+import com.sun.jdi.ClassObjectReference;
+import com.sun.jdi.ClassType;
+import com.sun.jdi.Field;
+import com.sun.jdi.IncompatibleThreadStateException;
+import com.sun.jdi.InvalidTypeException;
+import com.sun.jdi.InvocationException;
+import com.sun.jdi.Method;
+import com.sun.jdi.ObjectReference;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.StringReference;
+import com.sun.jdi.ThreadReference;
+import com.sun.jdi.Value;
+import com.sun.jdi.VirtualMachine;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyVetoException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.locks.Lock;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import org.netbeans.api.debugger.DebuggerManager;
+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.event.JPDABreakpointEvent;
+import org.netbeans.api.debugger.jpda.event.JPDABreakpointListener;
+
+import org.netbeans.modules.debugger.jpda.jdi.ArrayReferenceWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.ArrayTypeWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.ClassNotPreparedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.ClassObjectReferenceWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.ClassTypeWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.IllegalThreadStateExceptionWrapper;
+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.ReferenceTypeWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.ThreadReferenceWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.UnsupportedOperationExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.VMDisconnectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.VirtualMachineWrapper;
+import org.netbeans.modules.debugger.jpda.models.JPDAThreadImpl;
+
+import org.openide.util.Exceptions;
+import org.openide.util.RequestProcessor;
+import org.openide.util.WeakSet;
+
+/**
+ * Upload backend classed to the JVM.
+ */
+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 Map<JPDADebugger, ClassObjectReference> remoteServiceClasses = new WeakHashMap<>();
+    private static final Map<JPDADebugger, ThreadReference> remoteServiceAccess = new WeakHashMap<>();
+    
+    private static final RequestProcessor AUTORESUME_AFTER_SUSPEND_RP = new RequestProcessor("Autoresume after suspend", 1);
+    
+    private static final Set<PropertyChangeListener> serviceListeners = new WeakSet<>();
+
+    private RemoteServices() {}
+    
+    public static void addServiceListener(PropertyChangeListener listener) {
+        synchronized (serviceListeners) {
+            serviceListeners.add(listener);
+        }
+    }
+
+    private static void fireServiceClass(JPDADebugger debugger) {
+        PropertyChangeEvent pche = new PropertyChangeEvent(RemoteServices.class, "serviceClass", null, debugger);
+        PropertyChangeListener[] listeners;
+        synchronized (serviceListeners) {
+            listeners = serviceListeners.toArray(new PropertyChangeListener[]{});
+        }
+        for (PropertyChangeListener l : listeners) {
+            l.propertyChange(pche);
+        }
+    }
+    
+    private static ObjectReference getTruffleClassLoader(ThreadReference tawt, VirtualMachine vm) throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException, IOException, PropertyVetoException, InternalExceptionWrapper, VMDisconnectedExceptionWrapper, ObjectCollectedExceptionWrapper, UnsupportedOperationExceptionWrapper, ClassNotPreparedExceptionWrapper {
+        /* Use:
+           com.oracle.truffle.api.impl.TruffleLocator.class.getClassLoader()
+        */
+        ClassType truffleLocatorClass = getClass(vm, "com.oracle.truffle.api.impl.TruffleLocator");
+        return truffleLocatorClass.classLoader();
+    }
+    
+    private static ObjectReference getBootstrapClassLoader(ThreadReference tawt, VirtualMachine vm) throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException, IOException, PropertyVetoException, InternalExceptionWrapper, VMDisconnectedExceptionWrapper, ObjectCollectedExceptionWrapper, UnsupportedOperationExceptionWrapper, ClassNotPreparedExceptionWrapper {
+        /* Run this code:
+            ClassLoader cl = ClassLoader.getSystemClassLoader();
+            ClassLoader bcl;
+            do {
+                bcl = cl;
+                cl = cl.getParent();
+            } while (cl != null);
+            return bcl;
+         */
+        ClassType classLoaderClass = getClass(vm, ClassLoader.class.getName());
+        Method getSystemClassLoader = ClassTypeWrapper.concreteMethodByName(classLoaderClass, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
+        ObjectReference cl = (ObjectReference) ClassTypeWrapper.invokeMethod(classLoaderClass, tawt, getSystemClassLoader, Collections.emptyList(), ObjectReference.INVOKE_SINGLE_THREADED);
+        Method getParent = ClassTypeWrapper.concreteMethodByName(classLoaderClass, "getParent", "()Ljava/lang/ClassLoader;");
+        ObjectReference bcl;
+        do {
+            bcl = cl;
+            if ("sun.misc.Launcher$AppClassLoader".equals(cl.referenceType().name())) {     // NOI18N
+                break;
+            }
+            cl = (ObjectReference) ObjectReferenceWrapper.invokeMethod(cl, tawt, getParent, Collections.emptyList(), ObjectReference.INVOKE_SINGLE_THREADED);
+        } while (cl != null);
+        return bcl;
+    }
+    
+    private static ObjectReference getContextClassLoader(ThreadReference tawt, VirtualMachine vm) throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException, IOException, PropertyVetoException, InternalExceptionWrapper, VMDisconnectedExceptionWrapper, ObjectCollectedExceptionWrapper, UnsupportedOperationExceptionWrapper, ClassNotPreparedExceptionWrapper {
+        ReferenceType threadType = tawt.referenceType();
+        Method getContextCl = ClassTypeWrapper.concreteMethodByName((ClassType) threadType, "getContextClassLoader", "()Ljava/lang/ClassLoader;");
+        ObjectReference cl = (ObjectReference) ObjectReferenceWrapper.invokeMethod(tawt, tawt, getContextCl, Collections.emptyList(), ObjectReference.INVOKE_SINGLE_THREADED);
+        ClassType classLoaderClass = null;
+        if (cl == null) {
+            classLoaderClass = getClass(vm, ClassLoader.class.getName());
+            Method getSystemClassLoader = ClassTypeWrapper.concreteMethodByName(classLoaderClass, "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
+            cl = (ObjectReference) ClassTypeWrapper.invokeMethod(classLoaderClass, tawt, getSystemClassLoader, Collections.emptyList(), ObjectReference.INVOKE_SINGLE_THREADED);
+        }
+        return cl;
+    }
+    
+    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();
+        
+        t.notifyMethodInvoking();
+        try {
+            ClassObjectReference basicClass = null;
+            t.accessLock.writeLock().lock();
+            try {
+                List<RemoteClass> remoteClasses = getRemoteClasses();
+                for (RemoteClass rc : remoteClasses) {
+                    String className = rc.name;
+                    if (basicClass == null && className.endsWith(basicClassName) && className.indexOf('$') < 0) {
+                        List<ReferenceType> classesByName = VirtualMachineWrapper.classesByName(vm, className);
+                        if (!classesByName.isEmpty()) {
+                            basicClass = ReferenceTypeWrapper.classObject(classesByName.get(0));
+                        }
+                        break;
+                    }
+                }
+                // 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);
+                    }
+                }
+                if (basicClass != null) {
+                    // Initialize the class:
+                    ClassType theClass = getClass(vm, Class.class.getName());
+                    // Perhaps it's not 100% correct, we should be calling the new class' newInstance() method, not Class.newInstance() method.
+                    Method newInstance = ClassTypeWrapper.concreteMethodByName(theClass, "newInstance", "()Ljava/lang/Object;");
+                    ObjectReference newInstanceOfBasicClass = (ObjectReference) ObjectReferenceWrapper.invokeMethod(basicClass, tawt, newInstance, Collections.emptyList(), ObjectReference.INVOKE_SINGLE_THREADED);
+                }
+            } finally {
+                t.accessLock.writeLock().unlock();
+            }
+            if (basicClass != null) {
+                synchronized (remoteServiceClasses) {
+                    remoteServiceClasses.put(t.getDebugger(), basicClass);
+                    t.getDebugger().addPropertyChangeListener(new RemoteServiceDebuggerListener());
+                }
+                fireServiceClass(t.getDebugger());
+            }
+            return basicClass;
+        } finally {
+            t.notifyMethodInvokeDone();
+        }
+    }
+    
+    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();
+        final PropertyChangeListener[] listenerPtr = new PropertyChangeListener[] { null };
+        
+        mb.setBreakpointType(MethodBreakpoint.TYPE_METHOD_ENTRY);
+        mb.setSuspend(MethodBreakpoint.SUSPEND_EVENT_THREAD);
+        mb.setHidden(true);
+        mb.setThreadFilters(dbg, new JPDAThread[] { awtThread });
+        mb.addJPDABreakpointListener(new JPDABreakpointListener() {
+            @Override
+            public void breakpointReached(JPDABreakpointEvent event) {
+                if (dbg.equals(event.getDebugger())) {
+                    try {
+                        DebuggerManager.getDebuggerManager().removeBreakpoint(mb);
+                        //System.err.println("BREAKPOINT "+mb+" REMOVED after reached."+" ID = "+System.identityHashCode(mb));
+                        PropertyChangeListener listener = listenerPtr[0];
+                        if (listener != null) {
+                            dbg.removePropertyChangeListener(JPDADebugger.PROP_STATE, listener);
+                            listenerPtr[0] = null;
+                        }
+                        try {
+                            ((JPDAThreadImpl)awtThread).notifyMethodInvoking();
+                            runnable.run();
+                        } catch (PropertyVetoException e) {
+                        } finally {
+                            ((JPDAThreadImpl)awtThread).notifyMethodInvokeDone();
+                        }
+                    } finally {
+                        event.resume();
+                        latch.countDown();
+                    }
+                }
+            }
+        });
+        PropertyChangeListener listener = new PropertyChangeListener() {
+            @Override
+            public void propertyChange(PropertyChangeEvent evt) {
+                if (dbg.getState() == JPDADebugger.STATE_DISCONNECTED) {
+                    DebuggerManager.getDebuggerManager().removeBreakpoint(mb);
+                    //System.err.println("BREAKPOINT "+mb+" REMOVED after debugger finished."+" ID = "+System.identityHashCode(mb));
+                    dbg.removePropertyChangeListener(JPDADebugger.PROP_STATE, this);
+                    listenerPtr[0] = null;
+                    latch.countDown();
+                }
+            }
+        };
+        dbg.addPropertyChangeListener(JPDADebugger.PROP_STATE, listener);
+        listenerPtr[0] = listener;
+        if (dbg.getState() != JPDADebugger.STATE_DISCONNECTED) {
+            DebuggerManager.getDebuggerManager().addBreakpoint(mb);
+            //System.err.println("ADD BP: "+mb+" ID = "+System.identityHashCode(mb));
+        } else {
+            dbg.removePropertyChangeListener(JPDADebugger.PROP_STATE, listener);
+            //System.err.println("NOT ADDED BP: "+mb+" ID = "+System.identityHashCode(mb));
+            latch.countDown();
+        }
+    }
+    
+    private static final Map<JPDAThread, RequestProcessor.Task> tasksByThreads = new WeakHashMap<JPDAThread, RequestProcessor.Task> ();
+    
+    /**
+     * Run the provided runnable after the thread is assured to be stopped on an event.
+     * If the thread was initially running, it's resumed with some delay
+     * (to allow another execution of runOnStoppedThread() without the expensive thread preparation).
+     * It's assumed that the runnable will invoke methods on the thread.
+     * Therefore method invoke notification methods are executed automatically.
+     * @param thread The remote thread.
+     * @param run The Runnable that is executed when the thread is assured to be stopped on an event.
+     * @throws PropertyVetoException when can not invoke methods.
+     */
+    public static void runOnStoppedThread(JPDAThread thread, final Runnable run) throws PropertyVetoException {
+        final JPDAThreadImpl t = (JPDAThreadImpl) thread;
+        
+        Lock lock = t.accessLock.writeLock();
+        lock.lock();
+        try {
+            ThreadReference threadReference = t.getThreadReference();
+            boolean wasSuspended = t.isSuspended();
+            if (t.isSuspended() && !threadReference.isAtBreakpoint()) {
+                // TODO: Suspended, but will not be able to invoke methods
+                
+            }
+            if (!t.isSuspended()) {
+                final CountDownLatch latch = new CountDownLatch(1);
+                lock.unlock();
+                lock = null;
+                VirtualMachine vm = ((JPDAThreadImpl) thread).getThreadReference().virtualMachine();
+                ClassObjectReference serviceClassObject;
+                synchronized (remoteServiceClasses) {
+                    serviceClassObject = remoteServiceClasses.get(((JPDAThreadImpl) thread).getDebugger());
+                }
+                if (serviceClassObject == null) {
+                    // The debugger session has finished already, do not run anything.
+                    return ;
+                }
+                runOnBreakpoint(
+                    thread, 
+                    "org.netbeans.modules.debugger.jpda.visual.remote.RemoteAWTService", // NOI18N
+                    "calledInAWT", // NOI18N
+                    run,
+                    latch
+                );
+                try {
+                    ClassType serviceClass = (ClassType) ClassObjectReferenceWrapper.reflectedType(serviceClassObject);//getClass(vm, "org.netbeans.modules.debugger.jpda.visual.remote.RemoteService");
+                    Field awtAccess = ReferenceTypeWrapper.fieldByName(serviceClass, "awtAccess"); // NOI18N
+                    ClassTypeWrapper.setValue(serviceClass, awtAccess, VirtualMachineWrapper.mirrorOf(vm, true));
+                } catch (InternalExceptionWrapper iex) {
+                } catch (VMDisconnectedExceptionWrapper vmdex) {
+                } catch (Exception ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+                try {
+                    // wait for the async operation to finish
+                    latch.await();
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+                }
+            } else {
+                RequestProcessor.Task autoresumeTask;
+                if (!wasSuspended) {
+                    AutoresumeTask resumeTask = new AutoresumeTask(t);
+                    autoresumeTask = AUTORESUME_AFTER_SUSPEND_RP.create(resumeTask);
+                    synchronized (tasksByThreads) {
+                        tasksByThreads.put(thread, autoresumeTask);
+                    }
+                } else {
+                    synchronized (tasksByThreads) {
+                        autoresumeTask = tasksByThreads.get(thread);
+                    }
+                }
+                t.notifyMethodInvoking();
+                if (autoresumeTask != null) {
+                    autoresumeTask.schedule(Integer.MAX_VALUE); // wait for run.run() to finish...
+                }
+                try {
+                    run.run();
+                } finally {
+                    t.notifyMethodInvokeDone();
+                    if (autoresumeTask != null) {
+                        autoresumeTask.schedule(AutoresumeTask.WAIT_TIME);
+                    }
+                }
+            }
+        } finally {
+            if (lock != null) {
+                lock.unlock();
+            }
+        }
+    }
+    
+    private static final Map<JPDADebugger, LoggingListeners> loggingListeners =
+            new WeakHashMap<JPDADebugger, LoggingListeners>();
+    
+    private static final class LoggingListeners {
+        
+        private final Map<ObjectReference, Map<ClassObjectReference, Set<LoggingListenerCallBack>>> componentListeners =
+                new HashMap<ObjectReference, Map<ClassObjectReference, Set<LoggingListenerCallBack>>>();
+        
+        static LoggingListeners get(JPDADebugger dbg) {
+            synchronized (loggingListeners) {
+                return loggingListeners.get(dbg);
+            }
+        }
+
+        private synchronized boolean add(ObjectReference component, ClassObjectReference listenerClass, LoggingListenerCallBack listener) {
+            Map<ClassObjectReference, Set<LoggingListenerCallBack>> listeners = componentListeners.get(component);
+            if (listeners == null) {
+                listeners = new HashMap<ClassObjectReference, Set<LoggingListenerCallBack>>();
+                componentListeners.put(component, listeners);
+            }
+            Set<LoggingListenerCallBack> lcb = listeners.get(listenerClass);
+            if (lcb == null) {
+                lcb = new HashSet<LoggingListenerCallBack>();
+                listeners.put(listenerClass, lcb);
+            }
+            return lcb.add(listener);
+        }
+        
+        private synchronized boolean remove(ObjectReference component, ClassObjectReference listenerClass, LoggingListenerCallBack listener) {
+            Map<ClassObjectReference, Set<LoggingListenerCallBack>> listeners = componentListeners.get(component);
+            if (listeners == null) {
+                return false;
+            }
+            Set<LoggingListenerCallBack> lcb = listeners.get(listenerClass);
+            if (lcb == null) {
+                return false;
+            }
+            boolean removed = lcb.remove(listener);
+            if (removed) {
+                if (lcb.isEmpty()) {
+                    listeners.remove(listenerClass);
+                    if (listeners.isEmpty()) {
+                        componentListeners.remove(component);
+                    }
+                }
+            }
+            return removed;
+        }
+        
+        synchronized Set<LoggingListenerCallBack> getListeners(ObjectReference component, ClassObjectReference listenerClass) {
+            Map<ClassObjectReference, Set<LoggingListenerCallBack>> listeners = componentListeners.get(component);
+            if (listeners == null) {
+                return null;
+            }
+            return listeners.get(listenerClass);
+        }
+        
+        private synchronized boolean isEmpty() {
+            return componentListeners.isEmpty();
+        }
+        
+    }
+    
+    static void setAccessLoopStarted(JPDADebugger debugger, ThreadReference accessThread) {
+        synchronized (remoteServiceAccess) {
+            remoteServiceAccess.put(debugger, accessThread);
+        }
+        fireServiceClass(debugger);
+    }
+    
+    public static boolean interruptServiceAccessThread(JPDADebugger debugger) {
+        ClassObjectReference serviceClass = getServiceClass(debugger);
+        if (serviceClass != null) {
+            ThreadReference accessThread;
+            synchronized (remoteServiceAccess) {
+                accessThread = remoteServiceAccess.get(debugger);
+            }
+            if (accessThread == null) {
+                return false;
+            }
+            logger.fine("RemoteServices.interruptServiceAccessThread()");
+            try {
+                ClassType serviceClassType = (ClassType) ClassObjectReferenceWrapper.reflectedType(serviceClass);
+                Field accessLoopSleepingField = ReferenceTypeWrapper.fieldByName(serviceClassType, "accessLoopSleeping");
+                synchronized (accessThread) {
+                    boolean isSleeping;
+                    int repeatCheck = 10;
+                    do {
+                        BooleanValue sleepingValue = (BooleanValue) serviceClassType.getValue(accessLoopSleepingField);
+                        isSleeping = sleepingValue.booleanValue();
+                        if (isSleeping || --repeatCheck < 0) {
+                            break;
+                        } else {
+                            try {
+                                Thread.sleep(100);
+                            } catch (InterruptedException iex) {}
+                        }
+                        logger.log(Level.FINE, "  isSleeping = {0}", isSleeping);
+                    } while (!isSleeping);
+                    if (isSleeping) {
+                        ThreadReferenceWrapper.interrupt(accessThread);
+                        //System.err.println("  INTERRUPTED.");
+                        return true;
+                    }
+                }
+            } catch (InternalExceptionWrapper |
+                     VMDisconnectedExceptionWrapper |
+                     ObjectCollectedExceptionWrapper |
+                     ClassNotPreparedExceptionWrapper |
+                     IllegalThreadStateExceptionWrapper ex) {
+                logger.log(Level.FINE, "  NOT interrupted: ", ex);
+            }
+            logger.fine("  NOT Interrupted.");
+        }
+        return false;
+    }
+    
+    public static ClassObjectReference getServiceClass(JPDADebugger debugger) {
+        synchronized (remoteServiceClasses) {
+            return remoteServiceClasses.get(debugger);
+        }
+        
+    }
+    
+    static ClassType getClass(VirtualMachine vm, String name) throws InternalExceptionWrapper, VMDisconnectedExceptionWrapper, ObjectCollectedExceptionWrapper {
+        List<ReferenceType> classList = VirtualMachineWrapper.classesByName(vm, name);
+        ReferenceType clazz = null;
+        for (ReferenceType c : classList) {
+            if (ReferenceTypeWrapper.classLoader(c) == null) {
+                clazz = c;
+                break;
+            }
+        }
+        if (clazz == null && classList.size() > 0) {
+            clazz = classList.get(0);
+        }
+        return (ClassType) clazz;
+    }
+    
+    static ArrayType getArrayClass(VirtualMachine vm, String name) throws InternalExceptionWrapper, VMDisconnectedExceptionWrapper, ObjectCollectedExceptionWrapper {
+        List<ReferenceType> classList = VirtualMachineWrapper.classesByName(vm, name);
+        ReferenceType clazz = null;
+        for (ReferenceType c : classList) {
+            if (ReferenceTypeWrapper.classLoader(c) == null) {
+                clazz = c;
+                break;
+            }
+        }
+        return (ArrayType) clazz;
+    }
+    
+    private static List<RemoteClass> getRemoteClasses() throws IOException {
+        InputStream in = openRemoteClasses();
+        try {
+            ZipInputStream zin = new ZipInputStream(in);
+            ZipEntry ze;
+            List<RemoteClass> rcl = new ArrayList<RemoteClass>();
+            while((ze = zin.getNextEntry()) != null) {
+                String fileName = ze.getName();
+                if (!fileName.endsWith(".class")) {
+                    continue;
+                }
+                String name = fileName.substring(0, fileName.length() - ".class".length());
+                int baseStart = name.lastIndexOf('/');
+                if (baseStart < 0) {
+                    continue;
+                }
+                /*baseStart++;
+                int baseEnd = name.indexOf('$', baseStart);
+                if (baseEnd < 0) {
+                    baseEnd = name.length();
+                }*/
+                RemoteClass rc = new RemoteClass();
+                rc.name = name.replace('/', '.');
+                int l = (int) ze.getSize();
+                byte[] bytes = new byte[l];
+                int num = 0;
+                while (num < l) {
+                    int r = zin.read(bytes, num, l - num);
+                    if (r < 0) {
+                        Exceptions.printStackTrace(new IllegalStateException("Can not read full content of "+name+" entry. Length = "+l+", read num = "+num));
+                        break;
+                    }
+                    num += r;
+                }
+                rc.bytes = bytes;
+                rcl.add(rc);
+            }
+            return rcl;
+        } finally {
+            in.close();
+        }
+    }
+
+    static InputStream openRemoteClasses() {
+        return RemoteServices.class.getResourceAsStream(REMOTE_CLASSES_ZIPFILE);
+    }
+    
+    private static ArrayReference createTargetBytes(VirtualMachine vm, byte[] bytes,
+                                                    ByteValue[] mirrorBytesCache) 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);
+        for (int i = 0; i < bytes.length; i++) {
+            byte b = bytes[i];
+            ByteValue mb = mirrorBytesCache[128 + b];
+            if (mb == null) {
+                mb = VirtualMachineWrapper.mirrorOf(vm, b);
+                mirrorBytesCache[128 + b] = mb;
+            }
+            values.add(mb);
+        }
+        ArrayReferenceWrapper.setValues(array, values);
+        return array;
+    }
+    
+    private static class RemoteServiceDebuggerListener implements PropertyChangeListener {
+
+        @Override
+        public void propertyChange(PropertyChangeEvent evt) {
+            if (JPDADebugger.PROP_STATE.equals(evt.getPropertyName())) {
+                JPDADebugger d = (JPDADebugger) evt.getSource();
+                if (JPDADebugger.STATE_DISCONNECTED == d.getState()) {
+                    d.removePropertyChangeListener(this);
+                    synchronized (remoteServiceClasses) {
+                        remoteServiceClasses.remove(d);
+                    }
+                }
+            }
+        }
+        
+    }
+    
+    private static class RemoteClass {
+        private String name;
+        private byte[] bytes;
+    }
+    
+    public static class RemoteListener {
+        
+        private String type;
+        private List<String> allTypesList;
+        private String[] allTypes;
+        //private String classType;
+        private ObjectReference l;
+        
+        public RemoteListener(String type, ObjectReference l) {
+            this.type = type;
+            this.l = l;
+        }
+        
+        public String getType() {
+            return type;
+        }
+        
+        public void setAllTypes(String[] allTypes) {
+            this.allTypes = allTypes;
+        }
+        
+        private void addType(String listenerType) {
+            if (allTypesList == null) {
+                allTypesList = new ArrayList<String>();
+                allTypesList.add(type);
+            }
+            allTypesList.add(listenerType);
+        }
+        
+        public String[] getTypes() {
+            if (allTypes == null) {
+                if (allTypesList != null) {
+                    allTypes = allTypesList.toArray(new String[] {});
+                } else {
+                    allTypes = new String[] { type };
+                }
+            }
+            return allTypes;
+        }
+        
+        //public String getClassType() {
+        //    return classType;
+        //}
+        
+        public ObjectReference getListener() {
+            return l;
+        }
+
+        @Override
+        public String toString() {
+            return "RemoteListener("+type+")["+l+"]";
+        }
+
+    }
+    
+    public static interface LoggingListenerCallBack {
+        
+        public void eventsData(String[] data, String[] stack);
+        
+    }
+    
+    private static class AutoresumeTask implements Runnable, PropertyChangeListener {
+        
+        private static final int WAIT_TIME = 500;
+        
+        private volatile JPDAThreadImpl t;
+
+        public AutoresumeTask(JPDAThreadImpl t) {
+            this.t = t;
+            t.addPropertyChangeListener(this);
+        }
+
+        @Override
+        public void propertyChange(PropertyChangeEvent evt) {
+            JPDAThreadImpl thread = this.t;
+            if (thread == null) {
+                return ;
+            }
+            if (JPDAThread.PROP_SUSPENDED.equals(evt.getPropertyName()) &&
+                !"methodInvoke".equals(evt.getPropagationId())) {               // NOI18N
+                
+                thread.removePropertyChangeListener(this);
+                logger.fine("AutoresumeTask: autoresume canceled, thread changed suspended state: suspended = "+thread.isSuspended());
+                synchronized (tasksByThreads) {
+                    tasksByThreads.remove(thread);
+                }
+                t = null;
+            }
+        }
+        
+        @Override
+        public void run() {
+            JPDAThreadImpl thread = this.t;
+            this.t = null;
+            if (thread != null) {
+                thread.removePropertyChangeListener(this);
+                thread.resume();
+            }
+        }
+    }
+
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/StepIntoScriptHandler.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/StepIntoScriptHandler.java
new file mode 100644
index 0000000000..ad09a800d1
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/StepIntoScriptHandler.java
@@ -0,0 +1,124 @@
+/*
+ * 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.ClassObjectReference;
+import com.sun.jdi.ClassType;
+import com.sun.jdi.Field;
+import com.sun.jdi.InvalidTypeException;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.api.debugger.ActionsManager;
+import org.netbeans.api.debugger.ActionsManagerListener;
+import org.netbeans.api.debugger.LazyActionsManagerListener;
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.modules.debugger.jpda.jdi.ClassNotPreparedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.ClassObjectReferenceWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.ObjectCollectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.ReferenceTypeWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.VMDisconnectedExceptionWrapper;
+import org.netbeans.spi.debugger.ContextProvider;
+import org.openide.util.Exceptions;
+
+/**
+ * Handler of step into guest language from Java.
+ */
+@LazyActionsManagerListener.Registration(path="netbeans-JPDASession/Java")
+public class StepIntoScriptHandler extends LazyActionsManagerListener implements PropertyChangeListener {
+    
+    private static final Logger LOG = Logger.getLogger(StepIntoScriptHandler.class.getCanonicalName());
+    private static final String PROP_ACTION_TO_BE_RUN = "actionToBeRun";        // NOI18N
+    
+    private final JPDADebugger debugger;
+    private ClassType serviceClass;
+    private Field steppingField;
+    
+    public StepIntoScriptHandler(ContextProvider lookupProvider) {
+        debugger = lookupProvider.lookupFirst(null, JPDADebugger.class);
+        debugger.addPropertyChangeListener(JPDADebugger.PROP_CURRENT_CALL_STACK_FRAME, new CurrentSFTracker());
+    }
+
+    @Override
+    protected void destroy() {
+        LOG.fine("\nStepIntoJSHandler.destroy()");
+    }
+
+    @Override
+    public String[] getProperties() {
+        return new String[] { ActionsManagerListener.PROP_ACTION_PERFORMED, PROP_ACTION_TO_BE_RUN };
+    }
+
+    @Override
+    public void propertyChange(PropertyChangeEvent evt) {
+        if (PROP_ACTION_TO_BE_RUN.equals(evt.getPropertyName())) {
+            Object action = evt.getNewValue();
+            // Just before Java's step into runs:
+            if (ActionsManager.ACTION_STEP_INTO.equals(action)) {
+                ClassObjectReference serviceClassRef = RemoteServices.getServiceClass(debugger);
+                LOG.log(Level.FINE, "StepIntoScriptHandler.actionToBeRun: {0}, serviceClassRef = {1}", new Object[]{action, serviceClassRef});
+                if (serviceClassRef != null) {
+                    try {
+                        serviceClass = (ClassType) ClassObjectReferenceWrapper.reflectedType(serviceClassRef);
+                        steppingField = ReferenceTypeWrapper.fieldByName(serviceClass, "steppingIntoTruffle");
+                        serviceClass.setValue(steppingField, serviceClass.virtualMachine().mirrorOf(1));
+                        RemoteServices.interruptServiceAccessThread(debugger);
+                        LOG.fine("StepIntoScriptHandler: isSteppingInto set to true.");
+                    } catch (ClassNotLoadedException | ClassNotPreparedExceptionWrapper |
+                             InternalExceptionWrapper | InvalidTypeException |
+                             ObjectCollectedExceptionWrapper ex) {
+                        Exceptions.printStackTrace(ex);
+                    } catch (VMDisconnectedExceptionWrapper ex) {}
+                } else {
+                    // When the service is created, perform step into...
+                    DebugManagerHandler.execStepInto(debugger, true);
+                }
+            }
+        }
+    }
+    
+    private class CurrentSFTracker implements PropertyChangeListener {
+        @Override
+        public void propertyChange(PropertyChangeEvent evt) {
+            if (evt.getNewValue() == null) {
+                // Ignore resume.
+                return ;
+            }
+            LOG.fine("Current frame changed>");
+            if (steppingField != null) {
+                try {
+                    serviceClass.setValue(steppingField, serviceClass.virtualMachine().mirrorOf(-1));
+                    steppingField = null;
+                    RemoteServices.interruptServiceAccessThread(debugger);
+                    LOG.fine("StepIntoScriptHandler: isSteppingInto set to false.");
+                } catch (InvalidTypeException | ClassNotLoadedException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+            } else {
+                // Cancel step into when the service is created
+                DebugManagerHandler.execStepInto(debugger, false);
+            }
+        }
+    }
+
+}
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
new file mode 100644
index 0000000000..6f9b233e85
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/TruffleDebugManager.java
@@ -0,0 +1,376 @@
+/*
+ * 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.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;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.api.debugger.Breakpoint;
+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.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.
+ */
+@DebuggerServiceRegistration(types=LazyDebuggerManagerListener.class)
+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";
+    // Breakpoint on this class triggers search of existing engines
+    private static final String EXISTING_ENGINES_TRIGGER = "com.oracle.truffle.api.frame.FrameSlot";
+    
+    private JPDABreakpoint debugManagerLoadBP;
+    private static final Map<JPDADebugger, Boolean> haveExistingEnginesTrigger = new WeakHashMap<>();
+    private static final Map<JPDADebugger, DebugManagerHandler> dmHandlers = new HashMap<>();
+    private static final Map<JPDADebugger, JPDABreakpointListener> debugBPListeners = new HashMap<>();
+    
+    public TruffleDebugManager() {
+    }
+    
+    @Override
+    public Breakpoint[] initBreakpoints() {
+        initLoadBP();
+        return new Breakpoint[] { debugManagerLoadBP };
+    }
+    
+    private synchronized void initLoadBP() {
+        if (debugManagerLoadBP != null) {
+            return ;
+        }
+        /* Must NOT use a method exit breakpoint! It caused a massive degradation of application performance.
+        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.setHidden(true);
+        
+        LOG.log(Level.FINE, "TruffleDebugManager.initBreakpoints(): submitted BP {0}", debugManagerLoadBP);
+        TruffleAccess.init();
+    }
+
+    @Override
+    public void sessionAdded(Session session) {
+        JPDADebugger debugger = session.lookupFirst(null, JPDADebugger.class);
+        if (debugger == null) {
+            return ;
+        }
+        synchronized (dmHandlers) {
+            if (dmHandlers.containsKey(debugger)) {
+                // A new session for the same debugger?
+                return ;
+            }
+        }
+        initLoadBP();
+        JPDABreakpointListener bpl = addPolyglotEngineCreationBP(debugger);
+        LOG.log(Level.FINE, "TruffleDebugManager.sessionAdded({0}), adding BP listener to {1}", new Object[]{session, debugManagerLoadBP});
+        synchronized (debugBPListeners) {
+            debugBPListeners.put(debugger, bpl);
+        }
+    }
+
+    @Override
+    public void sessionRemoved(Session session) {
+        JPDADebugger debugger = session.lookupFirst(null, JPDADebugger.class);
+        if (debugger == null) {
+            return ;
+        }
+        JPDABreakpointListener bpl;
+        synchronized (debugBPListeners) {
+            bpl = debugBPListeners.remove(debugger);
+        }
+        if (bpl != null) {
+            LOG.log(Level.FINE, "TruffleDebugManager.engineRemoved({0}), removing BP listener from {1}", new Object[]{session, debugManagerLoadBP});
+            debugManagerLoadBP.removeJPDABreakpointListener(bpl);
+        }
+        DebugManagerHandler dmh;
+        synchronized (dmHandlers) {
+            dmh = dmHandlers.remove(debugger);
+        }
+        if (dmh != null) {
+            LOG.log(Level.FINE, "TruffleDebugManager.engineRemoved({0}), destroying {1}", new Object[]{session, dmh});
+            dmh.destroy();
+        }
+    }
+
+    private JPDABreakpointListener addPolyglotEngineCreationBP(final JPDADebugger debugger) {
+        JPDABreakpointListener bpl = new JPDABreakpointListener() {
+            @Override
+            public void breakpointReached(JPDABreakpointEvent event) {
+                try {
+                    submitPECreationBP(debugger, event.getReferenceType());
+                } finally {
+                    event.resume();
+                }
+            }
+        };
+        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... :-(
+            }
+            // Find possible existing instances of the engine
+            if (!enginePe.isEmpty() && debugger.canGetInstanceInfo()) {
+                long engineInstances = 0;
+                for (JPDAClassType pe : enginePe) {
+                    engineInstances += pe.getInstanceCount();
+                }
+                if (engineInstances > 0) {
+                    submitExistingEnginesProbe(debugger, enginePe);
+                }
+            }
+        };
+        if (debugger.getState() > 1) {
+            submitEngineCreation.run();
+        } else {
+            debugger.addPropertyChangeListener(JPDADebugger.PROP_STATE, new PropertyChangeListener() {
+                @Override
+                public void propertyChange(PropertyChangeEvent evt) {
+                    if (debugger.getState() > 1) {
+                        submitEngineCreation.run();
+                        debugger.removePropertyChangeListener(JPDADebugger.PROP_STATE, this);
+                    }
+                }
+            });
+        }
+        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);
+                    }
+                }
+            }
+        } catch (InternalExceptionWrapper | VMDisconnectedExceptionWrapper |
+                 ObjectCollectedExceptionWrapper | ClassNotPreparedExceptionWrapper ex) {
+        }
+    }
+
+    private void submitExistingEnginesProbe(final JPDADebugger debugger, List<JPDAClassType> enginePe) {
+        synchronized (haveExistingEnginesTrigger) {
+            if (Boolean.TRUE.equals(haveExistingEnginesTrigger.get(debugger))) {
+                return ;
+            }
+            haveExistingEnginesTrigger.put(debugger, Boolean.TRUE);
+        }
+        MethodBreakpoint execTrigger = MethodBreakpoint.create(EXISTING_ENGINES_TRIGGER, "*");
+        execTrigger.setHidden(true);
+        execTrigger.setSession(debugger);
+        execTrigger.addJPDABreakpointListener((event) -> {
+            DebuggerManager.getDebuggerManager().removeBreakpoint(execTrigger);
+            try {
+                JPDAThreadImpl thread = (JPDAThreadImpl) event.getThread();
+                boolean haveSomeEngine = false;
+                for (JPDAClassType pe : enginePe) {
+                    List<ObjectVariable> instances = pe.getInstances(0);
+                    for (ObjectVariable obj : instances) {
+                        Value value = ((JDIVariable) obj).getJDIValue();
+                        if (value instanceof ObjectReference) {
+                            haveNewPE(debugger, thread, (ObjectReference) value);
+                            haveSomeEngine = true;
+                        }
+                    }
+                }
+                if (haveSomeEngine) {
+                    for (ActionsProvider ap : ((JPDADebuggerImpl) debugger).getSession().lookup(null, ActionsProvider.class)) {
+                        if (ap.getActions().contains(PauseInGraalScriptActionProvider.NAME)) {
+                            // Trigger the enabling of the action as there are engines now:
+                            ap.isEnabled(PauseInGraalScriptActionProvider.NAME);
+                        }
+                    }
+                }
+            } finally {
+                event.resume();
+            }
+        });
+        DebuggerManager.getDebuggerManager().addBreakpoint(execTrigger);
+    }
+
+    private void haveNewPE(JPDADebugger debugger, JPDAThreadImpl thread, ObjectReference engine) {
+        DebugManagerHandler dmh;
+        synchronized (dmHandlers) {
+            dmh = dmHandlers.get(debugger);
+            if (dmh == null) {
+                dmh = new DebugManagerHandler(debugger);
+                dmHandlers.put(debugger, dmh);
+            }
+        }
+        dmh.newPolyglotEngineInstance(engine, thread);
+    }
+
+    @Override
+    public void breakpointAdded(Breakpoint breakpoint) {
+        if (breakpoint instanceof JSLineBreakpoint) {
+            Collection<DebugManagerHandler> handlers;
+            synchronized (dmHandlers) {
+                handlers = new ArrayList<>(dmHandlers.values());
+            }
+            for (DebugManagerHandler dmh : handlers) {
+                dmh.breakpointAdded((JSLineBreakpoint) breakpoint);
+            }
+        }
+    }
+
+    @Override
+    public void breakpointRemoved(Breakpoint breakpoint) {
+        if (breakpoint instanceof JSLineBreakpoint) {
+            Collection<DebugManagerHandler> handlers;
+            synchronized (dmHandlers) {
+                handlers = new ArrayList<>(dmHandlers.values());
+            }
+            for (DebugManagerHandler dmh : handlers) {
+                dmh.breakpointRemoved((JSLineBreakpoint) breakpoint);
+            }
+        }
+    }
+    
+    public static ClassType getDebugAccessorClass(JPDADebugger debugger) {
+        synchronized (dmHandlers) {
+            DebugManagerHandler dmh = dmHandlers.get(debugger);
+            if (dmh != null) {
+                return dmh.getAccessorClass();
+            } else {
+                return null;
+            }
+        }
+    }
+    
+    public static JPDAClassType getDebugAccessorJPDAClass(JPDADebugger debugger) {
+        synchronized (dmHandlers) {
+            DebugManagerHandler dmh = dmHandlers.get(debugger);
+            if (dmh != null) {
+                return dmh.getAccessorJPDAClass();
+            } else {
+                return null;
+            }
+        }
+    }
+
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/TruffleProperties.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/TruffleProperties.java
new file mode 100644
index 0000000000..73db135fa6
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/TruffleProperties.java
@@ -0,0 +1,136 @@
+/*
+ * 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 java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.function.Consumer;
+
+import org.netbeans.api.debugger.Properties;
+import org.openide.util.BaseUtilities;
+
+public final class TruffleProperties {
+    
+    private static final Properties truffleProperties = Properties.getDefault().getProperties("debugger.options.Truffle");  // NOI18N
+    private static final String PROP_SHOW_INTERNAL = "showInternal";            // NOI18N
+    private static TruffleProperties INSTANCE = new TruffleProperties();
+    
+    private TrufflePropertiesListener trufflePropertiesListener;
+    
+    private TruffleProperties() {}
+    
+    /*
+    public static TruffleProperties getInstance() {
+        return INSTANCE;
+    }
+    */
+    public boolean isShowInternal() {
+        return truffleProperties.getBoolean(PROP_SHOW_INTERNAL, false);
+    }
+    
+    public void setShowInternal(boolean showInternal) {
+        truffleProperties.setBoolean(PROP_SHOW_INTERNAL, showInternal);
+    }
+    
+    public synchronized Disposable onShowInternalChange(Consumer<Boolean> onChange) {
+        if (trufflePropertiesListener == null) {
+            trufflePropertiesListener = new TrufflePropertiesListener();
+            truffleProperties.addPropertyChangeListener(trufflePropertiesListener);
+        }
+        return trufflePropertiesListener.addOnShowInternalChange(onChange);
+    }
+    
+    public final class Disposable {
+        
+        private final LinkedList<?> list;
+        private final Consumer f;
+        private final DisposableReference ref;
+        
+        Disposable(LinkedList<?> list, Consumer f) {
+            this.list = list;
+            this.f = f;
+            ref = new DisposableReference(this, BaseUtilities.activeReferenceQueue());
+        }
+        
+        public void dispose() {
+            ref.dispose();
+            ref.clear();
+        }
+    }
+    
+    private static class DisposableReference extends WeakReference<Disposable> implements Runnable {
+        
+        private final LinkedList<?> list;
+        private final Consumer f;
+        
+        DisposableReference(Disposable disposable,  ReferenceQueue<? super Disposable> queue) {
+            super(disposable, queue);
+            this.list = disposable.list;
+            this.f = disposable.f;
+        }
+        
+        public void dispose() {
+            synchronized (list) {
+                list.remove(f);
+            }
+        }
+
+        @Override
+        public void run() {
+            dispose();
+        }
+    }
+    
+    private class TrufflePropertiesListener implements PropertyChangeListener {
+        
+        private LinkedList<Consumer<Boolean>> onChangeListeners = new LinkedList<>();
+
+        @Override
+        public void propertyChange(PropertyChangeEvent evt) {
+            String propertyName = evt.getPropertyName();
+            switch (propertyName) {
+                case PROP_SHOW_INTERNAL:
+                    Boolean isInternal = (Boolean) evt.getNewValue();
+                    List<Consumer<Boolean>> listeners;
+                    synchronized (onChangeListeners) {
+                        listeners = new ArrayList<>(onChangeListeners);
+                    }
+                    for (Consumer<Boolean> f : listeners) {
+                        f.accept(isInternal);
+                    }
+                    break;
+            }
+        }
+
+        private Disposable addOnShowInternalChange(Consumer<Boolean> onChange) {
+            synchronized (onChangeListeners) {
+                onChangeListeners.add(onChange);
+            }
+            return new Disposable(onChangeListeners, onChange);
+        }
+        
+    }
+    
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/Utils.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/Utils.java
new file mode 100644
index 0000000000..2917a83131
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/Utils.java
@@ -0,0 +1,64 @@
+/*
+ * 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 java.awt.Color;
+import javax.swing.JTable;
+import javax.swing.UIManager;
+
+public final class Utils {
+    
+    private Utils() {}
+    
+    public static String toHTML (
+        String text,
+        boolean bold,
+        boolean italics,
+        Color color
+    ) {
+        if (text == null) return null;
+        StringBuilder sb = new StringBuilder ();
+        sb.append ("<html>");
+        if (bold) sb.append ("<b>");
+        if (italics) sb.append ("<i>");
+        if (color == null) {
+            color = UIManager.getColor("Table.foreground");
+            if (color == null) {
+                color = new JTable().getForeground();
+            }
+        }
+        sb.append ("<font color=\"#");
+        String hexColor = Integer.toHexString ((color.getRGB () & 0xffffff));
+        for (int i = hexColor.length(); i < 6; i++) {
+            sb.append("0"); // Prepend zeros to length of 6
+        }
+        sb.append(hexColor);
+        sb.append ("\">");
+        text = text.replaceAll ("&", "&amp;");
+        text = text.replaceAll ("<", "&lt;");
+        text = text.replaceAll (">", "&gt;");
+        sb.append (text);
+        sb.append ("</font>");
+        if (italics) sb.append ("</i>");
+        if (bold) sb.append ("</b>");
+        sb.append ("</html>");
+        return sb.toString ();
+    }
+}
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
new file mode 100644
index 0000000000..6814e3500f
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/access/CurrentPCInfo.java
@@ -0,0 +1,115 @@
+/*
+ * 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.access;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.util.function.IntFunction;
+
+import org.netbeans.api.debugger.jpda.JPDAThread;
+import org.netbeans.api.debugger.jpda.LocalVariable;
+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;
+
+/**
+ * Container of information about the current program counter.
+ */
+public final class CurrentPCInfo {
+    
+    public static final String PROP_SELECTED_FRAME = "selectedFrame";           // NOI18N
+    
+    private final LocalVariable stepCmd;
+    private final Reference<JPDAThread> threadRef;
+    private final SourcePosition sp;
+    private final TruffleScope[] scopes;
+    private final TruffleStackFrame topFrame;
+    private final TruffleStackInfo stack;
+    private final IntFunction<TruffleNode> truffleNodes;
+    private volatile TruffleStackFrame selectedStackFrame; // the top frame initially
+    
+    private PropertyChangeSupport pchs = new PropertyChangeSupport(this);
+    
+    CurrentPCInfo(LocalVariable stepCmd, JPDAThread thread, SourcePosition sp,
+                  TruffleScope[] scopes, TruffleStackFrame topFrame,
+                  TruffleStackInfo stack, IntFunction<TruffleNode> truffleNodes) {
+        this.stepCmd = stepCmd;
+        this.threadRef = new WeakReference<>(thread);
+        this.sp = sp;
+        this.scopes = scopes;
+        this.topFrame = topFrame;
+        this.stack = stack;
+        this.truffleNodes = truffleNodes;
+        selectedStackFrame = topFrame;
+    }
+    
+    public LocalVariable getStepCommandVar() {
+        return stepCmd;
+    }
+    
+    public JPDAThread getThread() {
+        return threadRef.get();
+    }
+    
+    public SourcePosition getSourcePosition() {
+        return sp;
+    }
+
+    public TruffleScope[] getScopes() {
+        return scopes;
+    }
+    
+    public TruffleStackFrame getTopFrame() {
+        return topFrame;
+    }
+
+    public TruffleStackInfo getStack() {
+        return stack;
+    }
+
+    public TruffleStackFrame getSelectedStackFrame() {
+        return selectedStackFrame;
+    }
+
+    public void setSelectedStackFrame(TruffleStackFrame selectedStackFrame) {
+        TruffleStackFrame old = this.selectedStackFrame;
+        this.selectedStackFrame = selectedStackFrame;
+        if (old != selectedStackFrame) {
+            pchs.firePropertyChange(PROP_SELECTED_FRAME, old, selectedStackFrame);
+        }
+    }
+    
+    public TruffleNode getAST(TruffleStackFrame frame) {
+        return truffleNodes.apply(frame.getDepth());
+    }
+    
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+        pchs.addPropertyChangeListener(listener);
+    }
+    
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+        pchs.removePropertyChangeListener(listener);
+    }
+
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/access/ExecutionHaltedInfo.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/access/ExecutionHaltedInfo.java
new file mode 100644
index 0000000000..9a7787deff
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/access/ExecutionHaltedInfo.java
@@ -0,0 +1,64 @@
+/*
+ * 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.access;
+
+import org.netbeans.api.debugger.jpda.Field;
+import org.netbeans.api.debugger.jpda.LocalVariable;
+import org.netbeans.api.debugger.jpda.ObjectVariable;
+
+/**
+ * Halted information from the backend <code>JPDATruffleAccessor.executionHalted()</code>.
+ */
+final class ExecutionHaltedInfo {
+    
+    final ObjectVariable debugManager;
+    final ObjectVariable sourcePositions;
+    final boolean haltedBefore;
+    final ObjectVariable returnValue;
+    final ObjectVariable frameInfo;
+    final ObjectVariable[] breakpointsHit;
+    final ObjectVariable[] breakpointConditionExceptions;
+    final LocalVariable stepCmd;
+    
+    private ExecutionHaltedInfo(LocalVariable[] vars) {
+        this.debugManager = (ObjectVariable) vars[0];
+        this.sourcePositions = (ObjectVariable) vars[1];
+        this.haltedBefore = (Boolean) vars[2].createMirrorObject();
+        this.returnValue = (ObjectVariable) vars[3];
+        this.frameInfo = (ObjectVariable) vars[4];
+        this.breakpointsHit = getObjectArray((ObjectVariable) vars[5]);
+        this.breakpointConditionExceptions = getObjectArray((ObjectVariable) vars[6]);
+        this.stepCmd = vars[7];
+    }
+    
+    static ExecutionHaltedInfo get(LocalVariable[] vars) {
+        return new ExecutionHaltedInfo(vars);
+    }
+    
+    private static ObjectVariable[] getObjectArray(ObjectVariable var) {
+        Field[] fields = var.getFields(0, Integer.MAX_VALUE);
+        int n = fields.length;
+        ObjectVariable[] arr = new ObjectVariable[n];
+        for (int i = 0; i < n; i++) {
+            arr[i] = (ObjectVariable) fields[i];
+        }
+        return arr;
+    }
+}
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
new file mode 100644
index 0000000000..386d565356
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/access/TruffleAccess.java
@@ -0,0 +1,557 @@
+/*
+ * 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.access;
+
+import com.sun.jdi.AbsentInformationException;
+import com.sun.jdi.ClassType;
+import com.sun.jdi.InvocationException;
+import com.sun.jdi.StringReference;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyVetoException;
+import java.io.InvalidObjectException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.concurrent.locks.Lock;
+import java.util.function.Supplier;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.netbeans.api.debugger.DebuggerManager;
+import org.netbeans.api.debugger.jpda.CallStackFrame;
+import org.netbeans.api.debugger.jpda.Field;
+import org.netbeans.api.debugger.jpda.InvalidExpressionException;
+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.LocalVariable;
+import org.netbeans.api.debugger.jpda.MethodBreakpoint;
+import org.netbeans.api.debugger.jpda.ObjectVariable;
+import org.netbeans.api.debugger.jpda.Variable;
+import org.netbeans.api.debugger.jpda.event.JPDABreakpointEvent;
+import org.netbeans.api.debugger.jpda.event.JPDABreakpointListener;
+
+import org.netbeans.modules.debugger.jpda.expr.InvocationExceptionTranslated;
+import org.netbeans.modules.debugger.jpda.expr.JDIVariable;
+import org.netbeans.modules.debugger.jpda.models.JPDAClassTypeImpl;
+import org.netbeans.modules.debugger.jpda.models.JPDAThreadImpl;
+import org.netbeans.modules.debugger.jpda.truffle.RemoteServices;
+import org.netbeans.modules.debugger.jpda.truffle.TruffleDebugManager;
+import org.netbeans.modules.debugger.jpda.truffle.actions.StepActionProvider;
+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.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.TruffleVariable;
+import org.netbeans.modules.debugger.jpda.util.WeakHashMapActive;
+import org.openide.util.Exceptions;
+
+/**
+ * Access to the backend <code>JPDATruffleAccessor</code> class.
+ */
+public class TruffleAccess implements JPDABreakpointListener {
+    
+    private static final Logger LOG = Logger.getLogger(TruffleAccess.class.getName());
+    
+    public static final String BASIC_CLASS_NAME = "org.netbeans.modules.debugger.jpda.backend.truffle.JPDATruffleAccessor";    // NOI18N
+    
+    private static final String METHOD_EXEC_HALTED = "executionHalted";         // NOI18N
+    private static final String METHOD_EXEC_STEP_INTO = "executionStepInto";    // NOI18N
+    private static final String METHOD_DEBUGGER_ACCESS = "debuggerAccess";      // NOI18N
+    
+    private static final String VAR_NODE = "astNode";                           // NOI18N
+    private static final String VAR_FRAME = "frame";                            // NOI18N
+    private static final String VAR_SRC_ID = "id";                              // NOI18N
+    private static final String VAR_SRC_URI = "uri";                            // NOI18N
+    private static final String VAR_SRC_NAME = "name";                          // NOI18N
+    private static final String VAR_SRC_PATH = "path";                          // NOI18N
+    private static final String VAR_SRC_LINE = "line";                          // NOI18N
+    private static final String VAR_SRC_CODE = "code";
+    private static final String VAR_STACK_TRACE = "stackTrace";
+    private static final String VAR_TOP_FRAME = "topFrame";                     // NOI18N
+    private static final String VAR_TOP_VARS = "topVariables";                  // NOI18N
+    private static final String VAR_THIS_OBJECT = "thisObject";                 // NOI18N
+    
+    private static final String METHOD_GET_VARIABLES = "getVariables";          // NOI18N
+    private static final String METHOD_GET_VARIABLES_SGN = "(Lcom/oracle/truffle/api/debug/DebugStackFrame;)[Ljava/lang/Object;";  // NOI18N
+    private static final String METHOD_GET_SCOPE_VARIABLES = "getScopeVariables";// NOI18N
+    private static final String METHOD_GET_SCOPE_VARIABLES_SGN = "(Lcom/oracle/truffle/api/debug/DebugScope;)[Ljava/lang/Object;"; // NOI18N
+    private static final String METHOD_SET_UNWIND = "setUnwind";// NOI18N
+    private static final String METHOD_SET_UNWIND_SGN = "(I)Z"; // NOI18N
+    private static final String METHOD_GET_AST = "getTruffleAST";               // NOI18N
+    private static final String METHOD_GET_AST_SGN = "(I)[Ljava/lang/Object;";
+    
+    private static final Map<JPDAThread, ThreadInfo> currentPCInfos = new WeakHashMap<>();
+    private static final PropertyChangeListener threadResumeListener = new ThreadResumeListener();
+    
+    private static final TruffleAccess DEFAULT = new TruffleAccess();
+
+    private final Map<JPDADebugger, JPDABreakpoint> execHaltedBP = new WeakHashMapActive<>();
+    private final Map<JPDADebugger, JPDABreakpoint> execStepIntoBP = new WeakHashMapActive<>();
+    private final Map<JPDADebugger, JPDABreakpoint> dbgAccessBP = new WeakHashMapActive<>();
+    
+    private final Object methodCallAccessLock = new Object();//new ReentrantReadWriteLock(true).writeLock();
+    private MethodCallsAccess methodCallsRunnable;
+    private static final MethodCallsAccess METHOD_CALLS_SUCCESSFUL = new MethodCallsAccess(){@Override public void callMethods(JPDAThread thread) {}};
+    
+    private TruffleAccess() {}
+    
+    public static void init() {
+        DEFAULT.initBPs();
+    }
+    
+    public static void assureBPSet(JPDADebugger debugger, ClassType accessorClass) {
+        DEFAULT.execHaltedBP.put(debugger, DEFAULT.createBP(accessorClass.name(), METHOD_EXEC_HALTED, debugger));
+        DEFAULT.execStepIntoBP.put(debugger, DEFAULT.createBP(accessorClass.name(), METHOD_EXEC_STEP_INTO, debugger));
+        DEFAULT.dbgAccessBP.put(debugger, DEFAULT.createBP(accessorClass.name(), METHOD_DEBUGGER_ACCESS, debugger));
+    }
+    
+    private void initBPs() {
+        // Init debugger session-independent breakpoints
+    }
+    
+    private JPDABreakpoint createBP(String className, String methodName, JPDADebugger debugger) {
+        final MethodBreakpoint mb = MethodBreakpoint.create(className, methodName);
+        mb.setBreakpointType(MethodBreakpoint.TYPE_METHOD_ENTRY);
+        mb.setHidden(true);
+        mb.setSession(debugger);
+        mb.addJPDABreakpointListener(this);
+        DebuggerManager.getDebuggerManager().addBreakpoint(mb);
+        return mb;
+    }
+    
+    public static CurrentPCInfo getCurrentPCInfo(JPDAThread thread) {
+        ThreadInfo info;
+        synchronized (currentPCInfos) {
+            info = currentPCInfos.get(thread);
+        }
+        if (info != null) {
+            return info.cpi;
+        } else {
+            return null;
+        }
+    }
+
+    private static CurrentPCInfo getSomePCInfo(JPDADebugger dbg) {
+        synchronized (currentPCInfos) {
+            for (Map.Entry<JPDAThread, ThreadInfo> pce : currentPCInfos.entrySet()) {
+                if (((JPDAThreadImpl) pce.getKey()).getDebugger() == dbg) {
+                    CurrentPCInfo cpi = pce.getValue().cpi;
+                    if (cpi != null) {
+                        return cpi;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void breakpointReached(JPDABreakpointEvent event) {
+        Object bp = event.getSource();
+        JPDADebugger debugger = event.getDebugger();
+        if (execHaltedBP.get(debugger) == bp) {
+            LOG.log(Level.FINE, "TruffleAccessBreakpoints.breakpointReached({0}), exec halted.", event);
+            StepActionProvider.killJavaStep(debugger);
+            setCurrentPosition(debugger, event.getThread());
+        } else if (execStepIntoBP.get(debugger) == bp) {
+            LOG.log(Level.FINE, "TruffleAccessBreakpoints.breakpointReached({0}), exec step into.", event);
+            StepActionProvider.killJavaStep(debugger);
+            setCurrentPosition(debugger, event.getThread());
+        } else if (dbgAccessBP.get(debugger) == bp) {
+            LOG.log(Level.FINE, "TruffleAccessBreakpoints.breakpointReached({0}), debugger access.", event);
+            try {
+                synchronized (methodCallAccessLock) {
+                    if (methodCallsRunnable != null) {
+                        invokeMethodCalls(event.getThread(), methodCallsRunnable);
+                    }
+                    methodCallsRunnable = METHOD_CALLS_SUCCESSFUL;
+                    methodCallAccessLock.notifyAll();
+                }
+            } finally {
+                event.resume();
+            }
+        }
+    }
+    
+    private void setCurrentPosition(JPDADebugger debugger, JPDAThread thread) {
+        CurrentPCInfo cpci = getCurrentPosition(debugger, thread);
+        synchronized (currentPCInfos) {
+            ThreadInfo info = currentPCInfos.get(thread);
+            if (info == null) {
+                ((JPDAThreadImpl) thread).addPropertyChangeListener(JPDAThreadImpl.PROP_SUSPENDED, threadResumeListener);
+                info = new ThreadInfo();
+                currentPCInfos.put(thread, info);
+            }
+            info.cpi = cpci;
+        }
+    }
+
+    private CurrentPCInfo getCurrentPosition(JPDADebugger debugger, JPDAThread thread) {
+        try {
+            CallStackFrame csf = thread.getCallStack(0, 1)[0];
+            LocalVariable[] localVariables = csf.getLocalVariables();
+            ExecutionHaltedInfo haltedInfo = ExecutionHaltedInfo.get(localVariables);
+            //JPDAClassType debugAccessor = TruffleDebugManager.getDebugAccessorJPDAClass(debugger);
+            ObjectVariable sourcePositionVar = haltedInfo.sourcePositions;
+            SourcePosition sp = getSourcePosition(debugger, sourcePositionVar);
+            
+            ObjectVariable frameInfoVar = haltedInfo.frameInfo;
+            ObjectVariable frame = (ObjectVariable) frameInfoVar.getField(VAR_FRAME);
+            ObjectVariable topVars = (ObjectVariable) frameInfoVar.getField(VAR_TOP_VARS);
+            TruffleScope[] scopes = createScopes(debugger, topVars);
+            ObjectVariable stackTrace = (ObjectVariable) frameInfoVar.getField(VAR_STACK_TRACE);
+            String topFrameDescription = (String) frameInfoVar.getField(VAR_TOP_FRAME).createMirrorObject();
+            ObjectVariable thisObject = null;// TODO: (ObjectVariable) frameInfoVar.getField("thisObject");
+            TruffleStackFrame topFrame = new TruffleStackFrame(debugger, thread, 0, frame, topFrameDescription, null/*code*/, scopes, thisObject, true);
+            TruffleStackInfo stack = new TruffleStackInfo(debugger, thread, stackTrace);
+            return new CurrentPCInfo(haltedInfo.stepCmd, thread, sp, scopes, topFrame, stack, depth -> {
+                return getTruffleAST(debugger, (JPDAThreadImpl) thread, depth, sp.getLine(), stack);
+            });
+        } catch (AbsentInformationException | IllegalStateException ex) {
+            Exceptions.printStackTrace(ex);
+            return null;
+        }
+    }
+
+    private static TruffleNode getTruffleAST(JPDADebugger debugger, JPDAThreadImpl thread, int depth, int topLine, TruffleStackInfo stack) {
+        JPDAClassType debugAccessor = TruffleDebugManager.getDebugAccessorJPDAClass(debugger);
+        Lock lock = thread.accessLock.writeLock();
+        lock.lock();
+        try {
+            if (thread.getState() == JPDAThread.STATE_ZOMBIE) {
+                return null;
+            }
+            final boolean[] suspended = new boolean[] { false };
+            PropertyChangeListener threadChange = (event) -> {
+                synchronized (suspended) {
+                    suspended[0] = (Boolean) event.getNewValue();
+                    suspended.notifyAll();
+                }
+            };
+            thread.addPropertyChangeListener(JPDAThreadImpl.PROP_SUSPENDED, threadChange);
+            while (!thread.isSuspended()) {
+                lock.unlock();
+                lock = null;
+                synchronized (suspended) {
+                    if (!suspended[0]) {
+                        try {
+                            suspended.wait();
+                        } catch (InterruptedException ex) {}
+                    }
+                }
+                lock = thread.accessLock.writeLock();
+                lock.lock();
+            }
+            thread.removePropertyChangeListener(JPDAThreadImpl.PROP_SUSPENDED, threadChange);
+            Variable ast = ((JPDAClassTypeImpl) debugAccessor).invokeMethod(thread, METHOD_GET_AST,
+                                                      METHOD_GET_AST_SGN,
+                                                      new Variable[] { debugger.createMirrorVar(depth, true) });
+            Variable[] astInfo = ((ObjectVariable) ast).getFields(0, Integer.MAX_VALUE);
+            int line = (depth == 0) ? topLine : stack.getStackFrames(true)[depth].getSourcePosition().getLine();
+            return TruffleNode.newBuilder().nodes((String) astInfo[0].createMirrorObject()).currentLine(line).build();
+        } catch (InvalidExpressionException | InvalidObjectException | NoSuchMethodException ex) {
+            Exceptions.printStackTrace(ex);
+            return null;
+        } finally {
+            if (lock != null) {
+                lock.unlock();
+            }
+        }
+    }
+
+    public static SourcePosition getSourcePosition(JPDADebugger debugger, ObjectVariable sourcePositionVar) {
+        Field varSrcId = sourcePositionVar.getField(VAR_SRC_ID);
+        if (varSrcId == null) {
+            // sourcePositionVar represents null
+            return null;
+        }
+        long id = (Long) varSrcId.createMirrorObject();
+        int line = (Integer) sourcePositionVar.getField(VAR_SRC_LINE).createMirrorObject();
+        Source src = Source.getExistingSource(debugger, id);
+        if (src == null) {
+            String name = (String) sourcePositionVar.getField(VAR_SRC_NAME).createMirrorObject();
+            String path = (String) sourcePositionVar.getField(VAR_SRC_PATH).createMirrorObject();
+            URI uri = (URI) sourcePositionVar.getField(VAR_SRC_URI).createMirrorObject();
+            StringReference codeRef = (StringReference) ((JDIVariable) sourcePositionVar.getField(VAR_SRC_CODE)).getJDIValue();
+            src = Source.getSource(debugger, id, name, path, uri, codeRef);
+        }
+        return new SourcePosition(debugger, id, src, line);
+    }
+    
+    private static TruffleScope[] createScopes(JPDADebugger debugger, ObjectVariable varsArrVar) {
+        Field[] varsArr = varsArrVar.getFields(0, Integer.MAX_VALUE);
+        List<TruffleScope> scopes = new LinkedList<>();
+        int n = varsArr.length;
+        int i = 0;
+        if (i < n) {
+            String scopeName = (String) varsArr[i++].createMirrorObject();
+            boolean scopeFunction = (Boolean) varsArr[i++].createMirrorObject();
+            int numArgs = (Integer) varsArr[i++].createMirrorObject();
+            int numVars = (Integer) varsArr[i++].createMirrorObject();
+            TruffleVariable[] arguments = new TruffleVariable[numArgs];
+            i = fillVars(debugger, arguments, varsArr, i);
+            TruffleVariable[] variables = new TruffleVariable[numVars];
+            i = fillVars(debugger, variables, varsArr, i);
+            scopes.add(new TruffleScope(scopeName, scopeFunction, arguments, variables));
+        }
+        while (i < n) {
+            // There are further scopes, retrieved lazily
+            String scopeName = (String) varsArr[i++].createMirrorObject();
+            boolean scopeFunction = (Boolean) varsArr[i++].createMirrorObject();
+            boolean hasArgs = (Boolean) varsArr[i++].createMirrorObject();
+            boolean hasVars = (Boolean) varsArr[i++].createMirrorObject();
+            ObjectVariable scope = (ObjectVariable) varsArr[i++];
+            scopes.add(new TruffleScope(scopeName, scopeFunction, hasArgs, hasVars, debugger, scope));
+        }
+        return scopes.toArray(new TruffleScope[scopes.size()]);
+    }
+    
+    private static int fillVars(JPDADebugger debugger, TruffleVariable[] vars, Field[] varsArr, int i) {
+        for (int vi = 0; vi < vars.length; vi++) {
+            String name = (String) varsArr[i++].createMirrorObject();
+            String type = (String) varsArr[i++].createMirrorObject();
+            boolean readable = (Boolean) varsArr[i++].createMirrorObject();
+            boolean writable = (Boolean) varsArr[i++].createMirrorObject();
+            boolean internal = (Boolean) varsArr[i++].createMirrorObject();
+            String valueStr = (String) varsArr[i++].createMirrorObject();
+            Supplier<SourcePosition> valueSource = parseSourceLazy(debugger,
+                                                                   varsArr[i++],
+                                                                   (JDIVariable) varsArr[i++]);
+            Supplier<SourcePosition> typeSource = parseSourceLazy(debugger,
+                                                                  varsArr[i++],
+                                                                  (JDIVariable) varsArr[i++]);
+            ObjectVariable value = (ObjectVariable) varsArr[i++];
+            vars[vi] = new TruffleStackVariable(debugger, name, type, readable,
+                                                writable, internal, valueStr,
+                                                valueSource, typeSource, value);
+        }
+        return i;
+    }
+
+    public static TruffleVariable[][] getScopeArgsAndVars(JPDADebugger debugger, ObjectVariable debugScope) {
+        JPDAClassType debugAccessor = TruffleDebugManager.getDebugAccessorJPDAClass(debugger);
+        try {
+            Variable scopeVars = debugAccessor.invokeMethod(METHOD_GET_SCOPE_VARIABLES,
+                                                            METHOD_GET_SCOPE_VARIABLES_SGN,
+                                                            new Variable[] { debugScope });
+            Field[] varsArr = ((ObjectVariable) scopeVars).getFields(0, Integer.MAX_VALUE);
+            int n = varsArr.length;
+            int i = 0;
+            if (i < n) {
+                int numArgs = (Integer) varsArr[i++].createMirrorObject();
+                int numVars = (Integer) varsArr[i++].createMirrorObject();
+                TruffleVariable[] arguments = new TruffleVariable[numArgs];
+                i = fillVars(debugger, arguments, varsArr, i);
+                TruffleVariable[] variables = new TruffleVariable[numVars];
+                i = fillVars(debugger, variables, varsArr, i);
+                return new TruffleVariable[][] { arguments, variables };
+            }
+        } catch (InvalidExpressionException | NoSuchMethodException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+        return new TruffleVariable[][] { new TruffleVariable[] {}, new TruffleVariable[] {} };
+    }
+
+    private static Supplier<SourcePosition> parseSourceLazy(JPDADebugger debugger, Variable sourceDefVar, JDIVariable codeRefVar) {
+        return () -> parseSource(debugger,
+                                 (String) sourceDefVar.createMirrorObject(),
+                                 (StringReference) codeRefVar.getJDIValue());
+    }
+    
+    private static SourcePosition parseSource(JPDADebugger debugger, String sourceDef, StringReference codeRef) {
+        if (sourceDef == null) {
+            return null;
+        }
+        int sourceId;
+        String sourceName;
+        String sourcePath;
+        URI sourceURI;
+        int sourceLine;
+        try {
+            int i1 = 0;
+            int i2 = sourceDef.indexOf('\n', i1);
+            sourceId = Integer.parseInt(sourceDef.substring(i1, i2));
+            i1 = i2 + 1;
+            i2 = sourceDef.indexOf('\n', i1);
+            sourceName = sourceDef.substring(i1, i2);
+            i1 = i2 + 1;
+            i2 = sourceDef.indexOf('\n', i1);
+            sourcePath = sourceDef.substring(i1, i2);
+            i1 = i2 + 1;
+            i2 = sourceDef.indexOf('\n', i1);
+            try {
+                sourceURI = new URI(sourceDef.substring(i1, i2));
+            } catch (URISyntaxException usex) {
+                throw new IllegalStateException("Bad URI: "+sourceDef.substring(i1, i2), usex);
+            }
+            i1 = i2 + 1;
+            sourceLine = Integer.parseInt(sourceDef.substring(i1));
+        } catch (IndexOutOfBoundsException ioob) {
+            throw new IllegalStateException("var source definition='"+sourceDef+"'", ioob);
+        }
+        Source src = Source.getSource(debugger, sourceId, sourceName, sourcePath, sourceURI, codeRef);
+        return new SourcePosition(debugger, sourceId, src, sourceLine);
+    }
+    
+    public static TruffleScope[] createFrameScopes(final JPDADebugger debugger,
+                                                    //final Variable suspendedInfo,
+                                                    final Variable frameInstance) {
+        JPDAClassType debugAccessor = TruffleDebugManager.getDebugAccessorJPDAClass(debugger);
+        try {
+            Variable frameVars = debugAccessor.invokeMethod(METHOD_GET_VARIABLES,
+                                                            METHOD_GET_VARIABLES_SGN,
+                                                            new Variable[] { frameInstance });
+            TruffleScope[] scopes = createScopes(debugger, (ObjectVariable) frameVars);
+            return scopes;
+        } catch (InvalidExpressionException | NoSuchMethodException ex) {
+            Exceptions.printStackTrace(ex);
+            return new TruffleScope[] {};
+        }
+    }
+    
+    public static boolean unwind(JPDADebugger debugger, JPDAThread thread, int depth) {
+        JPDAClassType debugAccessor = TruffleDebugManager.getDebugAccessorJPDAClass(debugger);
+        try {
+            Variable arg = debugger.createMirrorVar(depth);
+            Variable res = ((JPDAClassTypeImpl) debugAccessor).invokeMethod(thread, METHOD_SET_UNWIND, METHOD_SET_UNWIND_SGN, new Variable[] { arg });
+            return (Boolean) res.createMirrorObject();
+        } catch (InvalidExpressionException | InvalidObjectException | NoSuchMethodException ex) {
+            Exceptions.printStackTrace(ex);
+            return false;
+        }
+    }
+
+    /**
+     * Safe access to method calls in the backend accessor class.
+     * @param methodCalls The runnable, that is called under write lock on the current thread.
+     * @return <code>true</code> when the runnable with method calls is executed,
+     *         <code>false</code> when method execution is not possible.
+     */
+    public static boolean methodCallingAccess(final JPDADebugger debugger, MethodCallsAccess methodCalls) {
+        synchronized (DEFAULT.methodCallAccessLock) {
+            while (DEFAULT.methodCallsRunnable != null) {
+                // we're already processing some method calls...
+                try {
+                    DEFAULT.methodCallAccessLock.wait();
+                } catch (InterruptedException ex) {
+                    return false;
+                }
+            }
+            CurrentPCInfo currentPCInfo = getSomePCInfo(debugger);
+            if (currentPCInfo != null) {
+                JPDAThread thread = currentPCInfo.getThread();
+                if (thread != null) {
+                    boolean success = invokeMethodCalls(thread, methodCalls);
+                    if (success) {
+                        return true;
+                    }
+                }
+            }
+            // Was not able to invoke methods
+            boolean interrupted = RemoteServices.interruptServiceAccessThread(debugger);
+            if (!interrupted) {
+                return false;
+            }
+            PropertyChangeListener finishListener = new PropertyChangeListener() {
+                @Override
+                public void propertyChange(PropertyChangeEvent evt) {
+                    if (JPDADebugger.STATE_DISCONNECTED == debugger.getState()) {
+                        synchronized (DEFAULT.methodCallAccessLock) {
+                            DEFAULT.methodCallAccessLock.notifyAll();
+                        }
+                    }
+                }
+            };
+            debugger.addPropertyChangeListener(JPDADebugger.PROP_STATE, finishListener);
+            DEFAULT.methodCallsRunnable = methodCalls;
+            try {
+                DEFAULT.methodCallAccessLock.wait();
+            } catch (InterruptedException ex) {
+                return false;
+            } finally {
+                debugger.removePropertyChangeListener(JPDADebugger.PROP_STATE, finishListener);
+            }
+            boolean success = (DEFAULT.methodCallsRunnable == METHOD_CALLS_SUCCESSFUL);
+            DEFAULT.methodCallsRunnable = null;
+            return success;
+        }
+    }
+    
+    private static boolean invokeMethodCalls(JPDAThread thread, MethodCallsAccess methodCalls) {
+        assert Thread.holdsLock(DEFAULT.methodCallAccessLock);
+        boolean invoking = false;
+        InvocationException iex = null;
+        try {
+            ((JPDAThreadImpl) thread).notifyMethodInvoking();
+            invoking = true;
+            methodCalls.callMethods(thread);
+            return true;
+        } catch (PropertyVetoException pvex) {
+            return false;
+        } catch (InvocationException ex) {
+            iex = ex;
+        } finally {
+            if (invoking) {
+                ((JPDAThreadImpl) thread).notifyMethodInvokeDone();
+            }
+        }
+        if (iex != null) {
+            Throwable ex = new InvocationExceptionTranslated(iex, ((JPDAThreadImpl) thread).getDebugger()).preload((JPDAThreadImpl) thread);
+            Exceptions.printStackTrace(Exceptions.attachMessage(ex, "Invoking "+methodCalls));
+        }
+        return false;
+    }
+
+    public static interface MethodCallsAccess {
+        
+        void callMethods(JPDAThread thread) throws InvocationException;
+        
+    }
+
+    private static final class ThreadInfo {
+        volatile CurrentPCInfo cpi;
+    }
+
+    private static final class ThreadResumeListener implements PropertyChangeListener {
+
+        @Override
+        public void propertyChange(PropertyChangeEvent evt) {
+            JPDAThreadImpl t = (JPDAThreadImpl) evt.getSource();
+            if (!(Boolean) evt.getNewValue() && !t.isMethodInvoking()) { // not suspended, resumed
+                synchronized (currentPCInfos) {
+                    ThreadInfo info = currentPCInfos.get(t);
+                    if (info != null) {
+                        info.cpi = null;
+                    }
+                }
+            }
+        }
+
+    }
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/access/TruffleEval.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/access/TruffleEval.java
new file mode 100644
index 0000000000..08c0e9cd19
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/access/TruffleEval.java
@@ -0,0 +1,72 @@
+/*
+ * 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.access;
+
+import java.io.InvalidObjectException;
+import org.netbeans.api.debugger.jpda.InvalidExpressionException;
+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.ObjectVariable;
+import org.netbeans.api.debugger.jpda.Variable;
+import org.netbeans.modules.debugger.jpda.truffle.TruffleDebugManager;
+import org.openide.util.Exceptions;
+import org.openide.util.NbBundle;
+
+/**
+ * <code>DebugStackFrame.eval()</code>.
+ */
+public class TruffleEval {
+    
+    private static final String METHOD_EVALUATE = "evaluate";                   // NOI18N
+    private static final String METHOD_EVALUATE_ON_FRAME_SIG =
+            "(Lcom/oracle/truffle/api/debug/DebugStackFrame;Ljava/lang/String;)Ljava/lang/Object;"; // NOI18N
+    
+    private TruffleEval() {}
+
+    @NbBundle.Messages("MSG_NoSuspend=No current suspend location.")
+    public static Variable evaluate(JPDADebugger debugger, String expression) throws InvalidExpressionException {
+        JPDAThread currentThread = debugger.getCurrentThread();
+        CurrentPCInfo currentPCInfo = TruffleAccess.getCurrentPCInfo(currentThread);
+        if (currentPCInfo == null) {
+            throw new InvalidExpressionException(Bundle.MSG_NoSuspend());
+        }
+        ObjectVariable stackFrameInstance = currentPCInfo.getSelectedStackFrame().getStackFrameInstance();
+        JPDAClassType debugAccessor = TruffleDebugManager.getDebugAccessorJPDAClass(debugger);
+        try {
+            Variable mirrorExpression = debugger.createMirrorVar(expression);
+            Variable valueVar = debugAccessor.invokeMethod(
+                    METHOD_EVALUATE,
+                    METHOD_EVALUATE_ON_FRAME_SIG,
+                    new Variable[] { stackFrameInstance,
+                                     mirrorExpression });
+            return valueVar;
+        } catch (InvalidObjectException | NoSuchMethodException ex) {
+            try {
+                return debugger.createMirrorVar(ex.getLocalizedMessage());
+            } catch (InvalidObjectException iex) {
+                Exceptions.printStackTrace(iex);
+                return null;
+            }
+            //return ex.getLocalizedMessage();
+        }
+    }
+
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/access/TruffleStrataProvider.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/access/TruffleStrataProvider.java
new file mode 100644
index 0000000000..a50f8bb6c9
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/access/TruffleStrataProvider.java
@@ -0,0 +1,72 @@
+/*
+ * 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.access;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.netbeans.modules.debugger.jpda.models.CallStackFrameImpl;
+import org.netbeans.modules.debugger.jpda.spi.StrataProvider;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
+
+/**
+ * Provider of an artificial stratum for GraalVM guest language script.
+ */
+@DebuggerServiceRegistration(path = "netbeans-JPDASession", types = { StrataProvider.class })
+public class TruffleStrataProvider implements StrataProvider {
+    
+    public static final String TRUFFLE_STRATUM = "GraalVM_Script";
+    
+    private static final String TRUFFLE_ACCESS_CLASS = TruffleAccess.BASIC_CLASS_NAME;
+    private static final String TRUFFLE_ACCESS_METHOD = "executionHalted";
+    
+    @Override
+    public String getDefaultStratum(CallStackFrameImpl csf) {
+        if (isInTruffleAccessPoint(csf)) {
+            return TRUFFLE_STRATUM;
+        }
+        return null;
+    }
+
+    @Override
+    public List<String> getAvailableStrata(CallStackFrameImpl csf) {
+        if (isInTruffleAccessPoint(csf)) {
+            return Collections.singletonList(TRUFFLE_STRATUM);
+        }
+        return null;
+    }
+    
+    private boolean isInTruffleAccessPoint(CallStackFrameImpl csf) {
+        return TRUFFLE_ACCESS_CLASS.equals(csf.getClassName()) &&
+               TRUFFLE_ACCESS_METHOD.equals(csf.getMethodName());
+    }
+
+    @Override
+    public int getStrataLineNumber(CallStackFrameImpl csf, String stratum) {
+        if (TRUFFLE_STRATUM.equals(stratum)) {
+            CurrentPCInfo currentPCInfo = TruffleAccess.getCurrentPCInfo(csf.getThread());
+            if (currentPCInfo != null) {
+                return currentPCInfo.getSourcePosition().getLine();
+            }
+        }
+        return csf.getLineNumber(stratum);
+    }
+    
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/actions/Bundle.properties b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/actions/Bundle.properties
new file mode 100644
index 0000000000..12782ca7b7
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/actions/Bundle.properties
@@ -0,0 +1,18 @@
+# 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.
+
+CTL_PauseInGraalScriptAction=Toggle Pause In GraalVM Script
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
new file mode 100644
index 0000000000..af7fffa2df
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/actions/PauseInGraalScriptActionProvider.java
@@ -0,0 +1,186 @@
+/*
+ * 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.actions;
+
+import com.sun.jdi.ClassNotLoadedException;
+import com.sun.jdi.ClassObjectReference;
+import com.sun.jdi.ClassType;
+import com.sun.jdi.IncompatibleThreadStateException;
+import com.sun.jdi.InvalidTypeException;
+import com.sun.jdi.InvocationException;
+import com.sun.jdi.Method;
+import com.sun.jdi.ObjectReference;
+import com.sun.jdi.ThreadReference;
+import java.lang.ref.WeakReference;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Logger;
+import javax.swing.Action;
+import javax.swing.SwingUtilities;
+
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.JPDAThread;
+import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
+import org.netbeans.modules.debugger.jpda.actions.JPDADebuggerActionProvider;
+import org.netbeans.modules.debugger.jpda.expr.InvocationExceptionTranslated;
+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.VMDisconnectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.models.JPDAThreadImpl;
+import org.netbeans.modules.debugger.jpda.truffle.RemoteServices;
+import org.netbeans.modules.debugger.jpda.truffle.TruffleDebugManager;
+import org.netbeans.modules.debugger.jpda.truffle.access.TruffleAccess;
+import org.netbeans.spi.debugger.ActionsProvider;
+import org.netbeans.spi.debugger.ContextProvider;
+import org.openide.awt.Actions;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+
+/**
+ *
+ */
+@ActionsProvider.Registration(path="netbeans-JPDASession", actions={PauseInGraalScriptActionProvider.NAME})
+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 boolean suspendState = false;
+
+    public PauseInGraalScriptActionProvider (ContextProvider lookupProvider) {
+        super((JPDADebuggerImpl) lookupProvider.lookupFirst(null, JPDADebugger.class));
+    }
+
+    @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;
+                    });
+                }
+            }
+        }
+    }
+
+    @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();
+            }
+        }
+    }
+
+    @Override
+    public Set getActions() {
+        return Collections.singleton(NAME);
+    }
+
+    /**
+     * Schedule suspend of a next execution via DebuggerSession.suspendNextExecution().
+     */
+    private void scheduleSuspend() {
+        LOG.fine("scheduleSuspend()");
+        ClassType debugAccessor = TruffleDebugManager.getDebugAccessorClass(debugger);
+        try {
+            final Method suspendNextExecutionMethod = ClassTypeWrapper.concreteMethodByName(
+                        debugAccessor,
+                        ACCESSOR_SUSPEND_NEXT_EXECUTION,
+                        ACCESSOR_SUSPEND_NEXT_EXECUTION_SIGNAT);
+            TruffleAccess.methodCallingAccess(debugger, new TruffleAccess.MethodCallsAccess() {
+                @Override
+                public void callMethods(JPDAThread thread) throws InvocationException {
+                    ThreadReference tr = ((JPDAThreadImpl) thread).getThreadReference();
+                    try {
+                        ClassTypeWrapper.invokeMethod(
+                                    debugAccessor,
+                                    tr,
+                                    suspendNextExecutionMethod,
+                                    Collections.emptyList(),
+                                    ObjectReference.INVOKE_SINGLE_THREADED);
+                    } catch (InvocationException iex) {
+                        Throwable ex = new InvocationExceptionTranslated(iex, (JPDADebuggerImpl) debugger).preload((JPDAThreadImpl) thread);
+                        Exceptions.printStackTrace(Exceptions.attachMessage(ex, "suspendNextExecution()"));
+                    } catch (InvalidTypeException | ClassNotLoadedException |
+                             IncompatibleThreadStateException |
+                             InternalExceptionWrapper | VMDisconnectedExceptionWrapper |
+                             ObjectCollectedExceptionWrapper ex) {
+                        Exceptions.printStackTrace(Exceptions.attachMessage(ex, "suspendNextExecution()"));
+                    }
+                }
+            });
+        } catch (ClassNotPreparedExceptionWrapper | InternalExceptionWrapper |
+                 VMDisconnectedExceptionWrapper ex) {
+        }
+    }
+
+    private void cancelSuspend() {
+        LOG.fine("cancelSuspend()");
+    }
+
+    public static Action createAction(Map<String,?> params) {
+        assert params.get("action").equals(NAME);
+        try {
+            ClassLoader classLoader = Lookup.getDefault().lookup(ClassLoader.class);
+            Class debuggerActionClass = classLoader.loadClass("org.netbeans.modules.debugger.ui.actions.DebuggerAction");
+            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);
+            actionReference = new WeakReference<>(action);
+            return action;
+        } catch (Exception ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+}
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
new file mode 100644
index 0000000000..0506b2ff7f
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/actions/RunToCursorActionProvider.java
@@ -0,0 +1,233 @@
+/*
+ * 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.actions;
+
+import com.sun.jdi.ArrayReference;
+import com.sun.jdi.ClassNotLoadedException;
+import com.sun.jdi.ClassType;
+import com.sun.jdi.IncompatibleThreadStateException;
+import com.sun.jdi.IntegerValue;
+import com.sun.jdi.InvalidTypeException;
+import com.sun.jdi.InvocationException;
+import com.sun.jdi.Method;
+import com.sun.jdi.ObjectReference;
+import com.sun.jdi.StringReference;
+import com.sun.jdi.ThreadReference;
+import com.sun.jdi.Value;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import org.netbeans.api.debugger.ActionsManager;
+import org.netbeans.api.debugger.DebuggerEngine;
+import org.netbeans.api.debugger.Session;
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.JPDAThread;
+import org.netbeans.modules.debugger.jpda.EditorContextBridge;
+import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
+import org.netbeans.modules.debugger.jpda.expr.InvocationExceptionTranslated;
+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.VMDisconnectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.models.JPDAThreadImpl;
+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.source.Source;
+import org.netbeans.spi.debugger.ActionsProvider;
+import org.netbeans.spi.debugger.ActionsProviderSupport;
+import org.netbeans.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.ui.EditorContextDispatcher;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.util.Exceptions;
+
+/**
+ * Currently JavaScript-specific Run to cursor action provider.
+ */
+@ActionsProvider.Registration(path="netbeans-JPDASession/"+TruffleStrataProvider.TRUFFLE_STRATUM,
+                              actions={"runToCursor"}, activateForMIMETypes={"text/javascript"})
+public class RunToCursorActionProvider extends ActionsProviderSupport {
+    
+    private static final String ACCESSOR_SET_ONE_SHOT_LINE_BREAKPOINT = "setOneShotLineBreakpoint"; // NOI18N
+    private static final String ACCESSOR_SET_ONE_SHOT_LINE_BREAKPOINT_SIGNAT =
+            "(L"+String.class.getName().replace('.', '/')+";I)[Lcom/oracle/truffle/api/debug/Breakpoint;";   // NOI18N
+    
+    private final JPDADebugger debugger;
+    private final Session session;
+    private final PropertyChangeListener stateChangeListener;
+    private volatile ArrayReference oneShotBreakpoint;
+    
+    public RunToCursorActionProvider(ContextProvider context) {
+        debugger = context.lookupFirst(null, JPDADebugger.class);
+        session = context.lookupFirst(null, Session.class);
+        stateChangeListener = new PropertyChangeListener() {
+            @Override
+            public void propertyChange(PropertyChangeEvent evt) {
+                checkEnabled();
+            }
+        };
+        debugger.addPropertyChangeListener(JPDADebugger.PROP_STATE, stateChangeListener);
+        EditorContextDispatcher.getDefault().addPropertyChangeListener("text/javascript", stateChangeListener);
+        checkEnabled();
+    }
+    
+    private void checkEnabled() {
+        DebuggerEngine truffleDbgEngine = session.getEngineForLanguage(TruffleStrataProvider.TRUFFLE_STRATUM);
+        if (truffleDbgEngine == null) {
+            setEnabled(ActionsManager.ACTION_RUN_TO_CURSOR, false);
+            return ;
+        }
+        ActionsManager actionsManager = truffleDbgEngine.getActionsManager();
+        int debuggerState = debugger.getState();
+        setEnabled (
+            ActionsManager.ACTION_RUN_TO_CURSOR,
+            actionsManager.isEnabled(ActionsManager.ACTION_CONTINUE) &&
+            (debuggerState== JPDADebugger.STATE_STOPPED) &&
+            (EditorContextBridge.getContext().getCurrentLineNumber () >= 0) && 
+            (EditorContextBridge.getContext().getCurrentURL ().endsWith (".js"))
+        );
+        if (debuggerState == JPDADebugger.STATE_DISCONNECTED) {
+            debugger.removePropertyChangeListener (JPDADebugger.PROP_STATE, stateChangeListener);
+            EditorContextDispatcher.getDefault().removePropertyChangeListener(stateChangeListener);
+        }
+    }
+
+    @Override
+    public Set getActions() {
+        return Collections.singleton (ActionsManager.ACTION_RUN_TO_CURSOR);
+    }
+    
+    @Override
+    public void doAction(Object action) {
+        if (oneShotBreakpoint != null) {
+            removeBreakpoints(oneShotBreakpoint);
+            oneShotBreakpoint = null;
+        }
+        FileObject fo = EditorContextDispatcher.getDefault().getCurrentFile();
+        if (fo == null) {
+            return ;
+        }
+        URI uri = Source.getTruffleInternalURI(fo);
+        if (uri == null) {
+            uri = fo.toURI();
+        }
+        //File file = FileUtil.toFile(fo);
+        //if (file == null) {
+//            return ;
+        //}
+        //final String path = file.getAbsolutePath();
+        int line = EditorContextDispatcher.getDefault().getCurrentLineNumber ();
+        submitOneShotBreakpoint(uri.toString(), line);
+        JPDAThread currentThread = debugger.getCurrentThread();
+        if (currentThread != null) {
+            currentThread.resume();
+        }
+        
+    }
+    
+    private void submitOneShotBreakpoint(final String uri, final int line) {
+        final ClassType debugAccessor = TruffleDebugManager.getDebugAccessorClass(debugger);
+        try {
+            final Method setLineBreakpointMethod = ClassTypeWrapper.concreteMethodByName(
+                    debugAccessor,
+                    ACCESSOR_SET_ONE_SHOT_LINE_BREAKPOINT,
+                    ACCESSOR_SET_ONE_SHOT_LINE_BREAKPOINT_SIGNAT);
+            TruffleAccess.methodCallingAccess(debugger, new TruffleAccess.MethodCallsAccess() {
+                @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 });
+                    try {
+                        ArrayReference ret = (ArrayReference) ClassTypeWrapper.invokeMethod(
+                                debugAccessor,
+                                tr,
+                                setLineBreakpointMethod,
+                                args,
+                                ObjectReference.INVOKE_SINGLE_THREADED);
+                        oneShotBreakpoint = ret;
+                    } catch (InvocationException iex) {
+                        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 |
+                             InternalExceptionWrapper | VMDisconnectedExceptionWrapper |
+                             ObjectCollectedExceptionWrapper ex) {
+                        Exceptions.printStackTrace(Exceptions.attachMessage(ex, "Setting one shot breakpoint to "+uri+":"+line));
+                    }
+                }
+            });
+        } catch (ClassNotPreparedExceptionWrapper | InternalExceptionWrapper |
+                 VMDisconnectedExceptionWrapper ex) {
+        }
+    }
+    
+    private void removeBreakpoints(final ArrayReference bps) {
+        final ClassType debugAccessor = TruffleDebugManager.getDebugAccessorClass(debugger);
+        try {
+            final Method removeLineBreakpointMethod = ClassTypeWrapper.concreteMethodByName(
+                    debugAccessor,
+                    TruffleBreakpointsHandler.ACCESSOR_REMOVE_BREAKPOINT,
+                    TruffleBreakpointsHandler.ACCESSOR_REMOVE_BREAKPOINT_SIGNAT);
+            TruffleAccess.methodCallingAccess(debugger, new TruffleAccess.MethodCallsAccess() {
+                @Override
+                public void callMethods(JPDAThread thread) {
+                    ThreadReference tr = ((JPDAThreadImpl) thread).getThreadReference();
+                    for (Value bpv : bps.getValues()) {
+                        ObjectReference bp = (ObjectReference) bpv;
+                        List<? extends Value> args = Arrays.asList(new Value[] { bp });
+                        try {
+                            ClassTypeWrapper.invokeMethod(
+                                    debugAccessor,
+                                    tr,
+                                    removeLineBreakpointMethod,
+                                    args,
+                                    ObjectReference.INVOKE_SINGLE_THREADED);
+                            //successPtr[0] = true;
+                        } catch (InvocationException iex) {
+                            Throwable ex = new InvocationExceptionTranslated(iex, (JPDADebuggerImpl) debugger).preload((JPDAThreadImpl) thread);
+                            Exceptions.printStackTrace(Exceptions.attachMessage(ex, "Removing one shot breakpoint "+bp));
+                        } catch (InvalidTypeException | ClassNotLoadedException |
+                                 IncompatibleThreadStateException |
+                                 InternalExceptionWrapper | VMDisconnectedExceptionWrapper |
+                                 ObjectCollectedExceptionWrapper ex) {
+                            Exceptions.printStackTrace(Exceptions.attachMessage(ex, "Removing one shot breakpoint "+bp));
+                        }
+                    }
+                }
+            });
+        } catch (ClassNotPreparedExceptionWrapper | InternalExceptionWrapper |
+                 VMDisconnectedExceptionWrapper ex) {
+        }
+        
+    }
+
+}
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
new file mode 100644
index 0000000000..d1f18ae061
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/actions/StepActionProvider.java
@@ -0,0 +1,158 @@
+/*
+ * 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.actions;
+
+import com.sun.jdi.VirtualMachine;
+import com.sun.jdi.request.EventRequestManager;
+import com.sun.jdi.request.StepRequest;
+
+import java.io.InvalidObjectException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import org.netbeans.api.debugger.ActionsManager;
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.JPDAThread;
+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.truffle.access.CurrentPCInfo;
+import org.netbeans.modules.debugger.jpda.truffle.access.TruffleAccess;
+import org.netbeans.modules.debugger.jpda.truffle.access.TruffleStrataProvider;
+import org.netbeans.spi.debugger.ActionsProvider;
+import org.netbeans.spi.debugger.ContextProvider;
+import org.openide.util.Exceptions;
+
+/**
+ * Stepping in the guest language.
+ */
+@ActionsProvider.Registration(path="netbeans-JPDASession/"+TruffleStrataProvider.TRUFFLE_STRATUM,
+                              actions={"stepInto", "stepOver", "stepOut", "continue"})
+public class StepActionProvider extends JPDADebuggerActionProvider {
+    
+    private static final Logger LOG = Logger.getLogger(StepActionProvider.class.getName());
+    
+    private static final Set<Object> ACTIONS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(new Object[] {
+            ActionsManager.ACTION_STEP_INTO,
+            ActionsManager.ACTION_STEP_OUT,
+            ActionsManager.ACTION_STEP_OVER,
+            ActionsManager.ACTION_CONTINUE
+    })));
+
+    public StepActionProvider (ContextProvider lookupProvider) {
+        super (
+            (JPDADebuggerImpl) lookupProvider.lookupFirst 
+                (null, JPDADebugger.class)
+        );
+    }
+    
+    @Override
+    protected void checkEnabled(int debuggerState) {
+        Iterator i = getActions ().iterator ();
+        JPDAThread currentThread = getDebuggerImpl().getCurrentThread();
+        while (i.hasNext ()) {
+            setEnabled (
+                i.next (),
+                (debuggerState == JPDADebugger.STATE_STOPPED) &&
+                (currentThread != null) &&
+                (TruffleAccess.getCurrentPCInfo(currentThread) != null)
+            );
+        }
+    }
+
+    @Override
+    public void doAction(Object action) {
+        LOG.fine("doAction("+action+")");
+        JPDADebuggerImpl debugger = getDebuggerImpl();
+        JPDAThread currentThread = debugger.getCurrentThread();
+        CurrentPCInfo currentPCInfo = TruffleAccess.getCurrentPCInfo(currentThread);
+        int stepCmd = 0;
+        if (ActionsManager.ACTION_CONTINUE.equals(action)) {
+            stepCmd = 0;
+        } else if (ActionsManager.ACTION_STEP_INTO.equals(action)) {
+            stepCmd = 1;
+        } else if (ActionsManager.ACTION_STEP_OVER.equals(action)) {
+            stepCmd = 2;
+        } else if (ActionsManager.ACTION_STEP_OUT.equals(action)) {
+            stepCmd = 3;
+        }
+        try {
+            currentPCInfo.getStepCommandVar().setFromMirrorObject((Integer) stepCmd);
+        } catch (InvalidObjectException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+        killJavaStep(debugger);
+        if (stepCmd > 0) {
+            debugger.resumeCurrentThread();
+        } else {
+            debugger.resume();
+        }
+    }
+    
+    /**
+     * Kill any pending Java step.
+     * @param debugger 
+     */
+    public static void killJavaStep(JPDADebugger debugger) {
+        killJavaStep((JPDADebuggerImpl) debugger);
+    }
+    
+    // Kill any pending Java step...
+    private static void killJavaStep(JPDADebuggerImpl debugger) {
+        VirtualMachine vm = debugger.getVirtualMachine();
+        if (vm == null) {
+            return ;
+        }
+        EventRequestManager erm = vm.eventRequestManager();
+        List<StepRequest> stepRequests;
+        try {
+            stepRequests = EventRequestManagerWrapper.stepRequests(erm);
+        } catch (InternalExceptionWrapper | VMDisconnectedExceptionWrapper ex) {
+            return ;
+        }
+        if (stepRequests.isEmpty()) {
+            return ;
+        }
+        stepRequests = new ArrayList<>(stepRequests);
+        for (StepRequest sr : stepRequests) {
+            try {
+                EventRequestManagerWrapper.deleteEventRequest(erm, sr);
+                debugger.getOperator().unregister(sr);
+            } catch (InternalExceptionWrapper | VMDisconnectedExceptionWrapper |
+                     InvalidRequestStateExceptionWrapper ex) {
+            }
+        }
+    }
+
+    @Override
+    public Set getActions() {
+        return ACTIONS;
+    }
+    
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/actions/ToggleBreakpointsInLanguagesActionProvider.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/actions/ToggleBreakpointsInLanguagesActionProvider.java
new file mode 100644
index 0000000000..f01c728796
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/actions/ToggleBreakpointsInLanguagesActionProvider.java
@@ -0,0 +1,160 @@
+/*
+ * 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.actions;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import org.netbeans.api.debugger.ActionsManager;
+import org.netbeans.api.debugger.Breakpoint;
+import org.netbeans.api.debugger.DebuggerManager;
+import org.netbeans.modules.debugger.jpda.truffle.MIMETypes;
+import org.netbeans.modules.debugger.jpda.truffle.breakpoints.TruffleLineBreakpoint;
+import org.netbeans.modules.javascript2.debug.EditorLineHandlerFactory;
+import org.netbeans.spi.debugger.ActionsProvider;
+import org.netbeans.spi.debugger.ActionsProviderSupport;
+import org.netbeans.spi.debugger.ui.EditorContextDispatcher;
+import org.openide.filesystems.FileObject;
+import org.openide.text.Line;
+import org.openide.util.WeakListeners;
+
+/**
+ * Toggle breakpoint in the guest language.
+ */
+@ActionsProvider.Registration(path="", actions={ "toggleBreakpoint" })
+public class ToggleBreakpointsInLanguagesActionProvider extends ActionsProviderSupport
+                                                        implements PropertyChangeListener {
+    
+    private static final Set<String> IGNORED_MIME_TYPES = new HashSet<>(
+            // We have JSLineBreakpoint in JavaScript
+            Arrays.asList("text/javascript", "text/x-java"));                     // NOI18N
+    
+    private volatile Line postedLine;
+    
+    public ToggleBreakpointsInLanguagesActionProvider() {
+        setEnabled(ActionsManager.ACTION_TOGGLE_BREAKPOINT, false);
+        EditorContextDispatcher.getDefault().addPropertyChangeListener(
+                WeakListeners.propertyChange(this, EditorContextDispatcher.getDefault()));
+        MIMETypes.getDefault().addPropertyChangeListener(
+                WeakListeners.propertyChange(this, MIMETypes.getDefault()));
+    }
+
+    @Override
+    public void postAction(Object action, final Runnable actionPerformedNotifier) {
+        assert action == ActionsManager.ACTION_TOGGLE_BREAKPOINT : action;
+        EditorContextDispatcher context = EditorContextDispatcher.getDefault();
+        postedLine = context.getCurrentLine();
+        if (postedLine == null) {
+            actionPerformedNotifier.run();
+            return ;
+        }
+        super.postAction(action, new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    actionPerformedNotifier.run();
+                } finally {
+                    postedLine = null;
+                }
+            }
+        });
+    }
+    
+    @Override
+    public void doAction(Object action) {
+        assert action == ActionsManager.ACTION_TOGGLE_BREAKPOINT : action;
+        DebuggerManager d = DebuggerManager.getDebuggerManager ();
+        Line line = postedLine;
+        if (line == null) {
+            line = EditorContextDispatcher.getDefault().getCurrentLine();
+            if (line == null) {
+                return ;
+            }
+        }
+        FileObject fo = line.getLookup().lookup(FileObject.class);
+        if (fo == null) {
+            return ;
+        }
+        if (IGNORED_MIME_TYPES.contains(fo.getMIMEType())) {
+            return ;
+        }
+        Set<String> mts = MIMETypes.getDefault().get();
+        if (!mts.contains(fo.getMIMEType())) {
+            return ;
+        }
+        toggleBreakpoint(fo, line);
+    }
+
+    @Override
+    public Set getActions() {
+        return Collections.singleton (ActionsManager.ACTION_TOGGLE_BREAKPOINT);
+    }
+
+    @Override
+    public void propertyChange(PropertyChangeEvent evt) {
+        String propertyName = evt.getPropertyName();
+        if (MIMETypes.PROP_MIME_TYPES.equals(propertyName)) {
+            // Platform MIME types changed, enable the action
+            // and load them when the action is invoked.
+            setEnabled(ActionsManager.ACTION_TOGGLE_BREAKPOINT, true);
+        } else if (EditorContextDispatcher.PROP_FILE.equals(propertyName)) {
+            boolean enabled = false;
+            FileObject fo = EditorContextDispatcher.getDefault().getCurrentFile();
+            if (fo != null && !IGNORED_MIME_TYPES.contains(fo.getMIMEType())) {
+                Set<String> mts = MIMETypes.getDefault().getCached();
+                if (mts == null || mts.contains(fo.getMIMEType())) {
+                    // When MIME types are not loaded yet, enable the action
+                    // and load them when the action is invoked.
+                    enabled = true;
+                }
+            }
+            setEnabled(ActionsManager.ACTION_TOGGLE_BREAKPOINT, enabled);
+        }
+    }
+    
+    private void toggleBreakpoint(FileObject fo, Line line) {
+        DebuggerManager d = DebuggerManager.getDebuggerManager();
+        boolean add = true;
+        int lineNumber = line.getLineNumber() + 1;
+        for (Breakpoint breakpoint : d.getBreakpoints()) {
+            if (breakpoint instanceof TruffleLineBreakpoint &&
+                ((TruffleLineBreakpoint) breakpoint).getFileObject().equals(fo) &&
+                ((TruffleLineBreakpoint) breakpoint).getLineNumber() == lineNumber) {
+                
+                d.removeBreakpoint(breakpoint);
+                add = false;
+                break;
+            }
+        }
+        if (add) {
+            d.addBreakpoint(createLineBreakpoint(line));
+        }
+        
+    }
+
+    private Breakpoint createLineBreakpoint(Line line) {
+        FileObject fo = line.getLookup().lookup(FileObject.class);
+        return new TruffleLineBreakpoint(EditorLineHandlerFactory.getHandler(fo, line.getLineNumber() + 1));
+    }
+    
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/ast/TruffleNode.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/ast/TruffleNode.java
new file mode 100644
index 0000000000..0eaeea1edc
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/ast/TruffleNode.java
@@ -0,0 +1,208 @@
+/*
+ * 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.ast;
+
+/**
+ * A representation of Truffle Node class.
+ */
+public final class TruffleNode {
+
+    private final String className;
+    private final String description;
+    private final String sourceURI;
+    private final int l1;
+    private final int c1;
+    private final int l2;
+    private final int c2;
+    private final String tags;
+    private final TruffleNode[] ch;
+    private boolean current;
+
+    public TruffleNode(String className, String description, String sourceURI, int l1, int c1, int l2, int c2, String tags, int numCh) {
+        this.className = className;
+        this.description = description;
+        this.sourceURI = sourceURI;
+        this.l1 = l1;
+        this.c1 = c1;
+        this.l2 = l2;
+        this.c2 = c2;
+        this.tags = tags;
+        this.ch = new TruffleNode[numCh];
+    }
+
+    private void setChild(int i, TruffleNode node) {
+        ch[i] = node;
+    }
+
+    public String getClassName() {
+        return className;
+    }
+
+    public String getClassSimpleName() {
+        int index = className.lastIndexOf('.');
+        if (index > 0) {
+            return className.substring(index + 1);
+        } else {
+            return className;
+        }
+    }
+
+    public String getTags() {
+        return tags;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public String getSourceURI() {
+        return sourceURI;
+    }
+
+    public int getStartLine() {
+        return l1;
+    }
+
+    public int getStartColumn() {
+        return c1;
+    }
+
+    public int getEndLine() {
+        return l2;
+    }
+
+    public int getEndColumn() {
+        return c2;
+    }
+
+    public TruffleNode[] getChildren() {
+        return ch;
+    }
+
+    /** This node is currently being executed. */
+    public boolean isCurrent() {
+        return current;
+    }
+
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    public static class Builder {
+
+        private TruffleNode node;
+        private int currentLine;
+
+        private Builder() {}
+
+        public Builder nodes(String nodes) {
+            StringLineReader slr = new StringLineReader(nodes);
+            node = parseNode(slr);
+            return this;
+        }
+
+        public TruffleNode build() {
+            if (currentLine > 0) {
+                markCurrent(node, currentLine);
+            }
+            return node;
+        }
+
+        /** Mark all node paths which are currently being executed as current. */
+        private boolean markCurrent(TruffleNode node, int currentLine) {
+            if (node.getChildren().length == 0) {
+                if (node.getStartLine() <= currentLine && currentLine <= node.getEndLine()) {
+                    node.current = true;
+                    return true;
+                } else {
+                    return false;
+                }
+            } else {
+                boolean isSomeCurrent = false;
+                for (TruffleNode ch : node.getChildren()) {
+                    if (markCurrent(ch, currentLine)) {
+                        isSomeCurrent = true;
+                    }
+                }
+                node.current = isSomeCurrent;
+                return isSomeCurrent;
+            }
+        }
+
+        private TruffleNode parseNode(StringLineReader slr) {
+            String className = slr.nextLine();
+            String description = slr.nextLine();
+            String sourceURI;
+            int l1, c1, l2, c2;
+            String ss = slr.nextLine();
+            if (ss.isEmpty()) {
+                sourceURI = null;
+                l1 = c1 = l2 = c2 = -1;
+            } else {
+                sourceURI = ss;
+                ss = slr.nextLine();
+                int i1 = 0;
+                int i2 = ss.indexOf(':');
+                l1 = Integer.parseInt(ss.substring(i1, i2));
+                i1 = i2 + 1;
+                i2 = ss.indexOf('-');
+                c1 = Integer.parseInt(ss.substring(i1, i2));
+                i1 = i2 + 1;
+                i2 = ss.indexOf(':', i1);
+                l2 = Integer.parseInt(ss.substring(i1, i2));
+                i1 = i2 + 1;
+                i2 = ss.length();
+                c2 = Integer.parseInt(ss.substring(i1, i2));
+            }
+            String tags = slr.nextLine();
+            int numCh = Integer.parseInt(slr.nextLine());
+            TruffleNode node = new TruffleNode(className, description, sourceURI, l1, c1, l2, c2, tags, numCh);
+            for (int i = 0; i < numCh; i++) {
+                node.setChild(i, parseNode(slr));
+            }
+            return node;
+        }
+
+        public Builder currentLine(int line) {
+            this.currentLine = line;
+            return this;
+        }
+
+        private static class StringLineReader {
+
+            private final String lines;
+            private int i = 0;
+
+            private StringLineReader(String lines) {
+                this.lines = lines;
+            }
+
+            String nextLine() {
+                int i2 = lines.indexOf('\n', i);
+                if (i2 < i) {
+                    return null;
+                }
+                String line = lines.substring(i, i2);
+                i = i2 + 1;
+                return line;
+            }
+        }
+    }
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/ast/model/ASTNodeModel.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/ast/model/ASTNodeModel.java
new file mode 100644
index 0000000000..87a4317789
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/ast/model/ASTNodeModel.java
@@ -0,0 +1,94 @@
+/*
+ * 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.ast.model;
+
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.modules.debugger.jpda.truffle.access.TruffleStrataProvider;
+import org.netbeans.modules.debugger.jpda.truffle.ast.TruffleNode;
+import org.netbeans.modules.debugger.jpda.truffle.ast.view.ASTView;
+import org.netbeans.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
+import org.netbeans.spi.viewmodel.ModelListener;
+import org.netbeans.spi.viewmodel.NodeModel;
+import org.netbeans.spi.viewmodel.UnknownTypeException;
+
+@DebuggerServiceRegistration(path="netbeans-JPDASession/"+TruffleStrataProvider.TRUFFLE_STRATUM+"/"+ASTView.AST_VIEW_NAME,
+                             types={ NodeModel.class })
+public class ASTNodeModel implements NodeModel {
+
+    private final JPDADebugger debugger;
+
+    public ASTNodeModel(ContextProvider lookupProvider) {
+        debugger = lookupProvider.lookupFirst(null, JPDADebugger.class);
+    }
+
+    @Override
+    public String getDisplayName(Object node) throws UnknownTypeException {
+        if (node instanceof TruffleNode) {
+            TruffleNode ast = (TruffleNode) node;
+            String label = ast.getClassSimpleName();
+            String tags = ast.getTags();
+            if (!tags.isEmpty()) {
+                label = '[' + tags + "] " + label;
+            }
+            int l1 = ast.getStartLine();
+            if (l1 >= 0) {
+                int c1 = ast.getStartColumn();
+                int l2 = ast.getEndLine();
+                int c2 = ast.getEndColumn();
+                label += " <"+l1+":"+c1+"-"+l2+":"+c2+">";
+            }
+            if (ast.getChildren().length == 0) {
+                if (ast.isCurrent()) {
+                    label = "<html><b>" + label + "</b></html>";
+                }
+            }
+            return label;
+        } else {
+            throw new UnknownTypeException(node);
+        }
+    }
+
+    @Override
+    public String getIconBase(Object node) throws UnknownTypeException {
+        return "org/netbeans/modules/debugger/resources/threadsView/RunningThread";
+    }
+
+    @Override
+    public String getShortDescription(Object node) throws UnknownTypeException {
+        if (node instanceof TruffleNode) {
+            TruffleNode ast = (TruffleNode) node;
+            return ast.getClassName() + " (" + ast.getDescription() +")";
+        } else {
+            throw new UnknownTypeException(node);
+        }
+    }
+
+    @Override
+    public void addModelListener(ModelListener l) {
+        
+    }
+
+    @Override
+    public void removeModelListener(ModelListener l) {
+        
+    }
+    
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/ast/model/ASTTreeExpansionModel.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/ast/model/ASTTreeExpansionModel.java
new file mode 100644
index 0000000000..85792b5c86
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/ast/model/ASTTreeExpansionModel.java
@@ -0,0 +1,59 @@
+/*
+ * 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.ast.model;
+
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.modules.debugger.jpda.truffle.access.TruffleStrataProvider;
+import org.netbeans.modules.debugger.jpda.truffle.ast.TruffleNode;
+import org.netbeans.modules.debugger.jpda.truffle.ast.view.ASTView;
+import org.netbeans.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
+import org.netbeans.spi.viewmodel.TreeExpansionModel;
+import org.netbeans.spi.viewmodel.UnknownTypeException;
+
+@DebuggerServiceRegistration(path="netbeans-JPDASession/"+TruffleStrataProvider.TRUFFLE_STRATUM+"/"+ASTView.AST_VIEW_NAME,
+                             types={ TreeExpansionModel.class })
+public class ASTTreeExpansionModel implements TreeExpansionModel {
+
+    private final JPDADebugger debugger;
+
+    public ASTTreeExpansionModel(ContextProvider lookupProvider) {
+        debugger = lookupProvider.lookupFirst(null, JPDADebugger.class);
+    }
+
+    @Override
+    public boolean isExpanded(Object node) throws UnknownTypeException {
+        if (node instanceof TruffleNode) {
+            TruffleNode ast = (TruffleNode) node;
+            return ast.isCurrent();
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public void nodeExpanded(Object node) {
+    }
+
+    @Override
+    public void nodeCollapsed(Object node) {
+    }
+
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/ast/model/ASTTreeModel.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/ast/model/ASTTreeModel.java
new file mode 100644
index 0000000000..56e27e1ec3
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/ast/model/ASTTreeModel.java
@@ -0,0 +1,111 @@
+/*
+ * 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.ast.model;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.JPDAThread;
+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.ast.TruffleNode;
+import org.netbeans.modules.debugger.jpda.truffle.ast.view.ASTView;
+import org.netbeans.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
+import org.netbeans.spi.viewmodel.ModelEvent;
+import org.netbeans.spi.viewmodel.ModelListener;
+import org.netbeans.spi.viewmodel.TreeModel;
+import org.netbeans.spi.viewmodel.UnknownTypeException;
+import org.openide.util.WeakListeners;
+
+@DebuggerServiceRegistration(path="netbeans-JPDASession/"+TruffleStrataProvider.TRUFFLE_STRATUM+"/"+ASTView.AST_VIEW_NAME,
+                             types={ TreeModel.class })
+public class ASTTreeModel implements TreeModel, PropertyChangeListener {
+
+    private final JPDADebugger debugger;
+    private final Set<ModelListener> listeners = new CopyOnWriteArraySet<>();
+
+    public ASTTreeModel(ContextProvider lookupProvider) {
+        debugger = lookupProvider.lookupFirst(null, JPDADebugger.class);
+        debugger.addPropertyChangeListener(JPDADebugger.PROP_CURRENT_CALL_STACK_FRAME, WeakListeners.propertyChange(this, JPDADebugger.PROP_CURRENT_CALL_STACK_FRAME, debugger));
+    }
+
+    @Override
+    public Object getRoot() {
+        return TreeModel.ROOT;
+    }
+
+    @Override
+    public Object[] getChildren(Object parent, int from, int to) throws UnknownTypeException {
+        if (parent == TreeModel.ROOT) {
+            JPDAThread currentThread = debugger.getCurrentThread();
+            CurrentPCInfo currentPCInfo = TruffleAccess.getCurrentPCInfo(currentThread);
+            if (currentPCInfo == null) {
+                return new Object[] {};
+            }
+            TruffleNode ast = currentPCInfo.getAST(currentPCInfo.getSelectedStackFrame());
+            if (ast != null) {
+                return new Object[] { ast };
+            } else {
+                return new Object[] {};
+            }
+        } else if (parent instanceof TruffleNode) {
+            return ((TruffleNode) parent).getChildren();
+        }
+        throw new UnknownTypeException(parent);
+    }
+
+    @Override
+    public boolean isLeaf(Object node) throws UnknownTypeException {
+        if (node == TreeModel.ROOT) {
+            return false;
+        } else if (node instanceof TruffleNode) {
+            return ((TruffleNode) node).getChildren().length == 0;
+        }
+        throw new UnknownTypeException(node);
+    }
+
+    @Override
+    public int getChildrenCount(Object node) throws UnknownTypeException {
+        return Integer.MAX_VALUE;
+    }
+
+    @Override
+    public void addModelListener(ModelListener l) {
+        listeners.add(l);
+    }
+
+    @Override
+    public void removeModelListener(ModelListener l) {
+        listeners.remove(l);
+    }
+
+    @Override
+    public void propertyChange(PropertyChangeEvent evt) {
+        ModelEvent event = new ModelEvent.TreeChanged(this);
+        for (ModelListener l : listeners) {
+            l.modelChanged(event);
+        }
+    }
+    
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/ast/view/ASTView.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/ast/view/ASTView.java
new file mode 100644
index 0000000000..9f1b10c323
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/ast/view/ASTView.java
@@ -0,0 +1,49 @@
+/*
+ * 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.ast.view;
+
+import org.netbeans.spi.debugger.ui.ViewFactory;
+import org.openide.util.NbBundle;
+import org.openide.windows.TopComponent;
+
+/**
+ * The AST view.
+ */
+public final class ASTView {
+
+    public static final String AST_VIEW_NAME = "TruffleASTDebugView";
+
+    private ASTView() {}
+
+    /**
+     * @deprecated  Do not call directly, invoked from module layer.
+     */
+    @Deprecated
+    @NbBundle.Messages({"CTL_AST_View=Truffle AST Node Tree", "CTL_AST_View_tooltip=Truffle AST Node Tree"})
+    public static TopComponent getASTView() {
+        return ViewFactory.getDefault().createViewTC(
+                "",
+                AST_VIEW_NAME,
+                "NetBeansDebuggerTruffleASTNode",
+                null,
+                Bundle.CTL_AST_View(),
+                Bundle.CTL_AST_View_tooltip());
+    }
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/ast/view/ASTViewManager.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/ast/view/ASTViewManager.java
new file mode 100644
index 0000000000..acf5cc7e2b
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/ast/view/ASTViewManager.java
@@ -0,0 +1,110 @@
+/*
+ * 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.ast.view;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import javax.swing.SwingUtilities;
+import org.netbeans.api.debugger.DebuggerManagerAdapter;
+import org.netbeans.api.debugger.LazyDebuggerManagerListener;
+import org.netbeans.api.debugger.Session;
+import org.netbeans.api.debugger.jpda.CallStackFrame;
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+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.options.TruffleOptions;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
+import org.openide.windows.TopComponent;
+import org.openide.windows.WindowManager;
+
+/**
+ * Manager of the ASTView.
+ */
+@DebuggerServiceRegistration(types=LazyDebuggerManagerListener.class)
+public class ASTViewManager extends DebuggerManagerAdapter {
+
+    private volatile boolean shouldOpenView;
+    private volatile boolean isAtTruffleLocation;
+    private final PropertyChangeListener propListenerHolder;    // Not to have the listener collected
+
+    public ASTViewManager() {
+        shouldOpenView = TruffleOptions.isLanguageDeveloperMode();
+        propListenerHolder = propEvent -> {
+            boolean develMode = TruffleOptions.isLanguageDeveloperMode();
+            shouldOpenView = develMode;
+            if (develMode) {
+                openIfCan();
+            } else {
+                closeView();
+            }
+        };
+        TruffleOptions.onLanguageDeveloperModeChange(propListenerHolder);
+    }
+
+    @Override
+    public void sessionAdded(Session session) {
+        JPDADebugger debugger = session.lookupFirst(null, JPDADebugger.class);
+        if (debugger == null) {
+            return ;
+        }
+        debugger.addPropertyChangeListener(JPDADebugger.PROP_CURRENT_CALL_STACK_FRAME, this);
+    }
+
+    @Override
+    public void sessionRemoved(Session session) {
+        JPDADebugger debugger = session.lookupFirst(null, JPDADebugger.class);
+        if (debugger == null) {
+            return ;
+        }
+        debugger.removePropertyChangeListener(JPDADebugger.PROP_CURRENT_CALL_STACK_FRAME, this);
+    }
+
+    @Override
+    public void propertyChange(PropertyChangeEvent evt) {
+        if (JPDADebugger.PROP_CURRENT_CALL_STACK_FRAME.equals(evt.getPropertyName())) {
+            CallStackFrame frame = (CallStackFrame) evt.getNewValue();
+            if (frame != null) {
+                CurrentPCInfo currentPCInfo = TruffleAccess.getCurrentPCInfo(frame.getThread());
+                isAtTruffleLocation = currentPCInfo != null;
+                openIfCan();
+            } else {
+                isAtTruffleLocation = false;
+            }
+        }
+    }
+
+    private void openIfCan() {
+        if (shouldOpenView && isAtTruffleLocation) {
+            SwingUtilities.invokeLater(() -> {
+                TopComponent tc = WindowManager.getDefault().findTopComponent(ASTView.AST_VIEW_NAME);
+                tc.open();
+                tc.requestVisible();
+            });
+            shouldOpenView = false;
+        }
+    }
+
+    private void closeView() {
+        SwingUtilities.invokeLater(() -> {
+            TopComponent tc = WindowManager.getDefault().findTopComponent(ASTView.AST_VIEW_NAME);
+            tc.close();
+        });
+    }
+}
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/TruffleBreakpointReader.java
new file mode 100644
index 0000000000..87104696db
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/breakpoints/TruffleBreakpointReader.java
@@ -0,0 +1,135 @@
+/*
+ * 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.breakpoints;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+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.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;
+
+/**
+ * Breakpoints storage.
+ */
+@DebuggerServiceRegistration(types={Properties.Reader.class})
+public class TruffleBreakpointReader implements Properties.Reader {
+
+    private static final Logger LOG = Logger.getLogger(TruffleBreakpointReader.class.getName());
+    
+    @Override
+    public String[] getSupportedClassNames() {
+        return new String[] {
+            TruffleLineBreakpoint.class.getName (), 
+        };
+    }
+
+    @Override
+    public Object read(String className, Properties properties) {
+        TruffleLineBreakpoint b = null;
+        if (className.equals(TruffleLineBreakpoint.class.getName())) {
+            String urlStr = properties.getString (TruffleLineBreakpoint.PROP_URL, null);
+            int lineNumber = properties.getInt (TruffleLineBreakpoint.PROP_LINE_NUMBER, 1);
+            try {
+                URL url = new URL(urlStr);
+                FileObject fo = URLMapper.findFileObject(url);
+                if (fo == null) {
+                    if (isTransientURL(url)) {
+                        EditorLineHandler line = EditorLineHandlerFactory.getHandler(url, lineNumber);
+                        b = new TruffleLineBreakpoint(line);
+                    } else {
+                        return null;
+                    }
+                } else {
+                    EditorLineHandler line = EditorLineHandlerFactory.getHandler(fo, lineNumber);
+                    if (line != null) {
+                        b = new TruffleLineBreakpoint(line);
+                    } else {
+                        return null;
+                    }
+                }
+            } catch (MalformedURLException ex) {
+                LOG.log(Level.CONFIG, "urlStr = "+urlStr, ex);
+                return null;
+            }
+            
+        }
+        if (b == null) {
+            throw new IllegalStateException("Unknown breakpoint type: \""+className+"\"");
+        }
+        b.setCondition(properties.getString(TruffleLineBreakpoint.PROP_CONDITION, null));
+        /*b.setPrintText (
+            properties.getString (JSBreakpoint.PROP_PRINT_TEXT, "")
+        );*/
+        b.setGroupName(
+            properties.getString (Breakpoint.PROP_GROUP_NAME, "")
+        );
+        int hitCountFilter = properties.getInt(Breakpoint.PROP_HIT_COUNT_FILTER, 0);
+        Breakpoint.HIT_COUNT_FILTERING_STYLE hitCountFilteringStyle;
+        if (hitCountFilter > 0) {
+            hitCountFilteringStyle = Breakpoint.HIT_COUNT_FILTERING_STYLE.values()
+                    [properties.getInt(Breakpoint.PROP_HIT_COUNT_FILTER+"_style", 0)]; // NOI18N
+        } else {
+            hitCountFilteringStyle = null;
+        }
+        b.setHitCountFilter(hitCountFilter, hitCountFilteringStyle);
+        if (properties.getBoolean (Breakpoint.PROP_ENABLED, true))
+            b.enable ();
+        else
+            b.disable ();
+        if (b.canHaveDependentBreakpoints()) {
+            // TODO
+        }
+        return b;
+    }
+    
+    private boolean isTransientURL(URL url) {
+        return Source.URL_PROTOCOL.equals(url.getProtocol());
+    }
+
+    @Override
+    public void write(Object object, Properties properties) {
+        TruffleLineBreakpoint b = (TruffleLineBreakpoint) object;
+        properties.setString (
+            Breakpoint.PROP_GROUP_NAME, 
+            b.getGroupName ()
+        );
+        properties.setBoolean (Breakpoint.PROP_ENABLED, b.isEnabled ());
+        properties.setInt(Breakpoint.PROP_HIT_COUNT_FILTER, b.getHitCountFilter());
+        Breakpoint.HIT_COUNT_FILTERING_STYLE style = b.getHitCountFilteringStyle();
+        properties.setInt(Breakpoint.PROP_HIT_COUNT_FILTER+"_style", style != null ? style.ordinal() : 0); // NOI18N
+        if (b.canHaveDependentBreakpoints()) {
+            // TODO
+        }
+        
+        properties.setString(TruffleLineBreakpoint.PROP_CONDITION, b.getCondition());
+        URL url = b.getURL();
+        int line = b.getLineNumber();
+        properties.setString(TruffleLineBreakpoint.PROP_URL, url.toExternalForm());
+        properties.setInt(TruffleLineBreakpoint.PROP_LINE_NUMBER, line);
+    }
+    
+}
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/TruffleBreakpointsHandler.java
new file mode 100644
index 0000000000..d9df0bbd4c
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/breakpoints/TruffleBreakpointsHandler.java
@@ -0,0 +1,445 @@
+/*
+ * 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.breakpoints;
+
+import com.sun.jdi.ArrayReference;
+import com.sun.jdi.ClassNotLoadedException;
+import com.sun.jdi.ClassType;
+import com.sun.jdi.IncompatibleThreadStateException;
+import com.sun.jdi.IntegerValue;
+import com.sun.jdi.InvalidTypeException;
+import com.sun.jdi.InvocationException;
+import com.sun.jdi.Method;
+import com.sun.jdi.ObjectReference;
+import com.sun.jdi.StringReference;
+import com.sun.jdi.ThreadReference;
+import com.sun.jdi.Value;
+import com.sun.jdi.VirtualMachine;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+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.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.VMDisconnectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.models.JPDAThreadImpl;
+import org.netbeans.modules.debugger.jpda.truffle.access.TruffleAccess;
+import org.netbeans.modules.debugger.jpda.truffle.source.Source;
+import org.netbeans.modules.javascript2.debug.breakpoints.JSLineBreakpoint;
+import org.openide.filesystems.FileObject;
+import org.openide.util.Exceptions;
+import org.openide.util.Mutex;
+
+/**
+ * Handler of guest language breakpoints.
+ */
+public class TruffleBreakpointsHandler {
+    
+    private static final Logger LOG = Logger.getLogger(TruffleBreakpointsHandler.class.getName());
+    
+    private static final String ACCESSOR_SET_LINE_BREAKPOINT = "setLineBreakpoint"; // NOI18N
+    private static final String ACCESSOR_SET_LINE_BREAKPOINT_SIGNAT =
+            "(Ljava/lang/String;IILjava/lang/String;)[Lcom/oracle/truffle/api/debug/Breakpoint;";   // NOI18N
+    private static final String ACCESSOR_SET_LINE_BREAKPOINT_MGR_SIGNAT =
+            "(Lorg/netbeans/modules/debugger/jpda/backend/truffle/JPDATruffleDebugManager;Ljava/lang/String;IILjava/lang/String;)Lcom/oracle/truffle/api/debug/Breakpoint;";   // NOI18N
+    public static final String ACCESSOR_REMOVE_BREAKPOINT = "removeBreakpoint"; // NOI18N
+    public static final String ACCESSOR_REMOVE_BREAKPOINT_SIGNAT = "(Ljava/lang/Object;)V";    // NOI18N
+    
+    private final JPDADebugger debugger;
+    private ClassType accessorClass;
+    
+    private volatile boolean initialBreakpointsSubmitted = false;
+    private final Map<JSLineBreakpoint, Set<ObjectReference>> breakpointsMap = new HashMap<>();
+    private final JSBreakpointPropertyChangeListener breakpointsChangeListener = new JSBreakpointPropertyChangeListener();
+    
+    public TruffleBreakpointsHandler(JPDADebugger debugger) {
+        this.debugger = debugger;
+    }
+
+    public void destroy() {
+        synchronized (breakpointsMap) {
+            for (JSLineBreakpoint jsbp : breakpointsMap.keySet()) {
+                jsbp.removePropertyChangeListener(breakpointsChangeListener);
+            }
+        }
+    }
+    
+    /**
+     * Call in method invoking
+     */
+    public void submitBreakpoints(ClassType accessorClass, ObjectReference debugManager, JPDAThreadImpl t) throws InvocationException {
+        assert t.isMethodInvoking();
+        this.accessorClass = accessorClass;
+        Breakpoint[] breakpoints = DebuggerManager.getDebuggerManager().getBreakpoints();
+        initialBreakpointsSubmitted = true;
+        if (LOG.isLoggable(Level.FINE)) {
+            LOG.log(Level.FINE, "DebugManagerHandler: Breakpoints to submit = {0}", breakpoints);
+        }
+        Map<JSLineBreakpoint, ObjectReference> bpsMap = new HashMap<>();
+        for (Breakpoint breakpoint : breakpoints) {
+            if (!(breakpoint instanceof JSLineBreakpoint)) {
+                continue;
+            }
+            JSLineBreakpoint bp = (JSLineBreakpoint) breakpoint;
+            FileObject fileObject = bp.getFileObject();
+            if (fileObject == null) {
+                continue;
+            }
+            URI uri = Source.getTruffleInternalURI(fileObject);
+            if (uri == null) {
+                uri = fileObject.toURI();
+            }
+            ObjectReference bpImpl;
+            if (bp.isEnabled()) {
+                bpImpl = setLineBreakpoint(debugManager, t, uri, bp.getLineNumber(),
+                                           getIgnoreCount(bp), bp.getCondition());
+            } else {
+                bpImpl = null;
+            }
+            bpsMap.put(bp, bpImpl);
+            bp.addPropertyChangeListener(breakpointsChangeListener);
+        }
+        synchronized (breakpointsMap) {
+            for (Map.Entry<JSLineBreakpoint, ObjectReference> bpEntry : bpsMap.entrySet()) {
+                Set<ObjectReference> impls = breakpointsMap.get(bpEntry.getKey());
+                if (impls == null) {
+                    impls = new HashSet<ObjectReference>();
+                    breakpointsMap.put(bpEntry.getKey(), impls);
+                }
+                impls.add(bpEntry.getValue());
+            }
+        }
+    }
+    
+    private static int getIgnoreCount(JSLineBreakpoint bp) {
+        int ignoreCount = 0;
+        if (Breakpoint.HIT_COUNT_FILTERING_STYLE.GREATER.equals(bp.getHitCountFilteringStyle())) {
+            ignoreCount = bp.getHitCountFilter();
+        }
+        return ignoreCount;
+    }
+    
+    private ObjectReference setLineBreakpoint(ObjectReference debugManager,
+                                              JPDAThreadImpl t, URI uri, int line,
+                                              int ignoreCount, String condition) throws InvocationException {
+        assert t.isMethodInvoking();
+        ThreadReference tr = t.getThreadReference();
+        VirtualMachine vm = tr.virtualMachine();
+        try {
+            Method setLineBreakpointMethod = ClassTypeWrapper.concreteMethodByName(
+                    accessorClass,
+                    ACCESSOR_SET_LINE_BREAKPOINT,
+                    ACCESSOR_SET_LINE_BREAKPOINT_MGR_SIGNAT);
+            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());
+            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[] { debugManager, uriRef, lineRef, icRef, conditionRef });
+            ObjectReference ret = (ObjectReference) ClassTypeWrapper.invokeMethod(
+                    accessorClass,
+                    tr,
+                    setLineBreakpointMethod,
+                    args,
+                    ObjectReference.INVOKE_SINGLE_THREADED);
+            return ret;
+        } catch (VMDisconnectedExceptionWrapper | InternalExceptionWrapper |
+                 ClassNotLoadedException | ClassNotPreparedExceptionWrapper |
+                 IncompatibleThreadStateException | InvalidTypeException |
+                 ObjectCollectedExceptionWrapper ex) {
+            Exceptions.printStackTrace(Exceptions.attachMessage(ex, "Setting breakpoint to "+uri+":"+line));
+            return null;
+        }
+    }
+    
+    private void submitBP(JSLineBreakpoint bp) {
+        FileObject fileObject = bp.getFileObject();
+        if (fileObject == null) {
+            return ;
+        }
+        final URI uri;
+        URI tiuri = Source.getTruffleInternalURI(fileObject);
+        if (tiuri != null) {
+            uri = tiuri;
+        } else {
+            uri = fileObject.toURI();
+        }
+        final int line = bp.getLineNumber();
+        final int ignoreCount = getIgnoreCount(bp);
+        final String condition = bp.getCondition();
+        final ArrayReference[] bpRef = new ArrayReference[] { null };
+        if (bp.isEnabled()) {
+            try {
+                final Method setLineBreakpointMethod = ClassTypeWrapper.concreteMethodByName(
+                        accessorClass,
+                        ACCESSOR_SET_LINE_BREAKPOINT,
+                        ACCESSOR_SET_LINE_BREAKPOINT_SIGNAT);
+                if (setLineBreakpointMethod == null) {
+                    throw new IllegalStateException("Method "+ACCESSOR_SET_LINE_BREAKPOINT+" with signature:\n"+ACCESSOR_SET_LINE_BREAKPOINT_SIGNAT+"\nis not present in accessor class "+accessorClass);
+                }
+                TruffleAccess.methodCallingAccess(debugger, new TruffleAccess.MethodCallsAccess() {
+                    @Override
+                    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 });
+                        try {
+                            ArrayReference ret = (ArrayReference) ClassTypeWrapper.invokeMethod(
+                                    accessorClass,
+                                    tr,
+                                    setLineBreakpointMethod,
+                                    args,
+                                    ObjectReference.INVOKE_SINGLE_THREADED);
+                            bpRef[0] = ret;
+                        } catch (InvalidTypeException | ClassNotLoadedException |
+                                 IncompatibleThreadStateException |
+                                 InternalExceptionWrapper | VMDisconnectedExceptionWrapper |
+                                 ObjectCollectedExceptionWrapper ex) {
+                            Exceptions.printStackTrace(Exceptions.attachMessage(ex, "Setting breakpoint to "+uri+":"+line));
+                        }
+                    }
+                });
+            } catch (ClassNotPreparedExceptionWrapper | InternalExceptionWrapper |
+                     VMDisconnectedExceptionWrapper ex) {
+            }
+        }
+        bp.addPropertyChangeListener(breakpointsChangeListener);
+        List<Value> values = bpRef[0].getValues();
+        Set<ObjectReference> breakpoints = new HashSet<>();
+        for (Value v : values) {
+            if (v instanceof ObjectReference) {
+                breakpoints.add((ObjectReference) v);
+            }
+        }
+        if (!breakpoints.isEmpty()) {
+            synchronized (breakpointsMap) {
+                breakpointsMap.put(bp, breakpoints);
+            }
+        }
+    }
+    
+    private boolean removeBP(final JSLineBreakpoint bp) {
+        bp.removePropertyChangeListener(breakpointsChangeListener);
+        final Set<ObjectReference> bpImpls;
+        synchronized (breakpointsMap) {
+            bpImpls = breakpointsMap.remove(bp);
+        }
+        if (bpImpls == null) {
+            return false;
+        }
+        final boolean[] successPtr = new boolean[] { false };
+        try {
+            final Method removeLineBreakpointMethod = ClassTypeWrapper.concreteMethodByName(
+                    accessorClass,
+                    ACCESSOR_REMOVE_BREAKPOINT,
+                    ACCESSOR_REMOVE_BREAKPOINT_SIGNAT);
+            TruffleAccess.methodCallingAccess(debugger, new TruffleAccess.MethodCallsAccess() {
+                @Override
+                public void callMethods(JPDAThread thread) throws InvocationException {
+                    ThreadReference tr = ((JPDAThreadImpl) thread).getThreadReference();
+                    try {
+                        for (ObjectReference bpImpl : bpImpls) {
+                            List<? extends Value> args = Arrays.asList(new Value[] { bpImpl });
+                            try {
+                                ClassTypeWrapper.invokeMethod(
+                                        accessorClass,
+                                        tr,
+                                        removeLineBreakpointMethod,
+                                        args,
+                                        ObjectReference.INVOKE_SINGLE_THREADED);
+                                successPtr[0] = true;
+                            } catch (InvalidTypeException | ClassNotLoadedException |
+                                     IncompatibleThreadStateException |
+                                     InternalExceptionWrapper |
+                                     ObjectCollectedExceptionWrapper ex) {
+                                Exceptions.printStackTrace(ex);
+                            }
+                        }
+                    } catch (VMDisconnectedExceptionWrapper ex) {}
+                }
+            });
+        } catch (ClassNotPreparedExceptionWrapper | InternalExceptionWrapper |
+                 VMDisconnectedExceptionWrapper ex) {
+        }
+        return successPtr[0];
+    }
+    
+    private boolean setBreakpointProperty(JSLineBreakpoint bp,
+                                          TruffleBPMethods method,
+                                          final List<? extends Value> args) {
+        final Set<ObjectReference> bpImpls;
+        synchronized (breakpointsMap) {
+            bpImpls = breakpointsMap.get(bp);
+        }
+        if (bpImpls == null) {
+            if (bp.isEnabled()) {
+                submitBP(bp);
+                return true;
+            } else {
+                return false;
+            }
+        }
+        final boolean[] successPtr = new boolean[] { false };
+        try {
+            final Method setBreakpointPropertyMethod = ClassTypeWrapper.concreteMethodByName(
+                    (ClassType) ObjectReferenceWrapper.referenceType(bpImpls.iterator().next()),
+                    method.getMethodName(),
+                    method.getMethodSignature());
+            TruffleAccess.methodCallingAccess(debugger, new TruffleAccess.MethodCallsAccess() {
+                @Override
+                public void callMethods(JPDAThread thread) throws InvocationException {
+                    ThreadReference tr = ((JPDAThreadImpl) thread).getThreadReference();
+                    try {
+                        for (ObjectReference bpImpl : bpImpls) {
+                            try {
+                                ObjectReferenceWrapper.invokeMethod(
+                                        bpImpl,
+                                        tr,
+                                        setBreakpointPropertyMethod,
+                                        args,
+                                        ObjectReference.INVOKE_SINGLE_THREADED);
+                                successPtr[0] = true;
+                            } catch (InvalidTypeException | ClassNotLoadedException |
+                                     IncompatibleThreadStateException |
+                                     InternalExceptionWrapper |
+                                     ObjectCollectedExceptionWrapper ex) {
+                                Exceptions.printStackTrace(ex);
+                            }
+                        }
+                    } catch (VMDisconnectedExceptionWrapper ex) {}
+                }
+            });
+        } catch (ClassNotPreparedExceptionWrapper | InternalExceptionWrapper |
+                 ObjectCollectedExceptionWrapper | VMDisconnectedExceptionWrapper ex) {
+        }
+        return successPtr[0];
+    }
+
+    public void breakpointAdded(JSLineBreakpoint jsLineBreakpoint) {
+        if (initialBreakpointsSubmitted) {
+            // Breakpoints were submitted already, submit this as well.
+            if (Mutex.EVENT.isReadAccess()) {
+                // Lazy submit when called from UI
+                ((JPDADebuggerImpl) debugger).getRequestProcessor().post(() -> submitBP(jsLineBreakpoint));
+            } else {
+                submitBP(jsLineBreakpoint);
+            }
+        }
+    }
+
+    public void breakpointRemoved(JSLineBreakpoint jsLineBreakpoint) {
+        if (initialBreakpointsSubmitted) {
+            // Breakpoints were submitted already, remove this.
+            if (Mutex.EVENT.isReadAccess()) {
+                // Lazy remove when called from UI
+                ((JPDADebuggerImpl) debugger).getRequestProcessor().post(() -> removeBP(jsLineBreakpoint));
+            } else {
+                removeBP(jsLineBreakpoint);
+            }
+        }
+    }
+    
+    private static enum TruffleBPMethods {
+        setEnabled,
+        setIgnoreCount,
+        setCondition;
+        
+        public String getMethodName() {
+            return name();
+        }
+        
+        public String getMethodSignature() {
+            switch (this) {
+                case setEnabled:
+                    return "(Z)V";
+                case setIgnoreCount:
+                    return "(I)V";
+                case setCondition:
+                    return "(Ljava/lang/String;)V";
+                default:
+                    throw new IllegalStateException(this.name());
+            }
+        }
+    }
+    
+    private class JSBreakpointPropertyChangeListener implements PropertyChangeListener {
+        
+        @Override
+        public void propertyChange(PropertyChangeEvent evt) {
+            final JSLineBreakpoint jsbp = (JSLineBreakpoint) evt.getSource();
+            String propertyName = evt.getPropertyName();
+            final TruffleBPMethods method;
+            final List<? extends Value> args;
+            final VirtualMachine vm = ((JPDADebuggerImpl) debugger).getVirtualMachine();
+            if (vm == null) {
+                return ;
+            }
+            switch (propertyName) {
+                case JSLineBreakpoint.PROP_ENABLED:
+                    method = TruffleBPMethods.setEnabled;
+                    args = Collections.singletonList(vm.mirrorOf(jsbp.isEnabled()));
+                    break;
+                case JSLineBreakpoint.PROP_CONDITION:
+                    method = TruffleBPMethods.setCondition;
+                    String condition = jsbp.getCondition();
+                    StringReference conditionRef = (condition != null) ? vm.mirrorOf(condition) : null;
+                    args = Collections.singletonList(conditionRef);
+                    break;
+                case Breakpoint.PROP_HIT_COUNT_FILTER:
+                    method = TruffleBPMethods.setIgnoreCount;
+                    args = Collections.singletonList(vm.mirrorOf(getIgnoreCount(jsbp)));
+                    break;
+                default:
+                    return ;
+            }
+            ((JPDADebuggerImpl) debugger).getRequestProcessor().post(new Runnable() {
+                @Override
+                public void run() {
+                    setBreakpointProperty(jsbp, method, args);
+                }
+            });
+        }
+        
+    }
+}
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
new file mode 100644
index 0000000000..2a083a7c46
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/breakpoints/TruffleLineBreakpoint.java
@@ -0,0 +1,30 @@
+/*
+ * 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.breakpoints;
+
+import org.netbeans.modules.javascript2.debug.EditorLineHandler;
+import org.netbeans.modules.javascript2.debug.breakpoints.JSLineBreakpoint;
+
+public class TruffleLineBreakpoint extends JSLineBreakpoint {
+    
+    public TruffleLineBreakpoint(EditorLineHandler lineHandler) {
+        super(lineHandler);
+    }
+}
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
new file mode 100644
index 0000000000..d3e6c44463
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/TruffleStackFrame.java
@@ -0,0 +1,198 @@
+/*
+ * 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.frames;
+
+import com.sun.jdi.StringReference;
+import java.io.InvalidObjectException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.api.debugger.jpda.InvalidExpressionException;
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.JPDAThread;
+import org.netbeans.api.debugger.jpda.ObjectVariable;
+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.actions.StepActionProvider;
+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.openide.util.Exceptions;
+
+/**
+ *
+ * @author Martin
+ */
+public final class TruffleStackFrame {
+
+    private static final Logger LOG = Logger.getLogger(TruffleStackFrame.class.getName());
+    
+    private final JPDADebugger debugger;
+    private final JPDAThread thread;
+    private final int depth;
+    private final ObjectVariable frameInstance;
+    private final String methodName;
+    private final String sourceLocation;
+    
+    private final int    sourceId;
+    private final String sourceName;
+    private final String sourcePath;
+    private final URI    sourceURI;
+    private final int    sourceLine;
+    private final StringReference codeRef;
+    private TruffleScope[] scopes;
+    private final ObjectVariable thisObject;
+    private final boolean isInternal;
+    
+    public TruffleStackFrame(JPDADebugger debugger, JPDAThread thread, int depth,
+                             ObjectVariable frameInstance,
+                             String frameDefinition, StringReference codeRef,
+                             TruffleScope[] scopes, ObjectVariable thisObject,
+                             boolean includeInternal) {
+        if (LOG.isLoggable(Level.FINE)) {
+            try {
+                LOG.fine("new TruffleStackFrame("+depth+", "+
+                         frameInstance.getToStringValue()+" of type "+frameInstance.getClassType().getName()+
+                         ", "+frameDefinition+", vars = "+Arrays.toString(scopes)+
+                         ", "+thisObject+")");
+            } catch (InvalidExpressionException iex) {
+                LOG.log(Level.FINE, iex.getMessage(), iex);
+            }
+        }
+        this.debugger = debugger;
+        this.thread = thread;
+        this.depth = depth;
+        this.frameInstance = frameInstance;
+        boolean internalFrame = includeInternal;
+        try {
+            int i1 = 0;
+            int i2 = frameDefinition.indexOf('\n');
+            methodName = frameDefinition.substring(i1, i2);
+            i1 = i2 + 1;
+            i2 = frameDefinition.indexOf('\n', i1);
+            sourceLocation = frameDefinition.substring(i1, i2);
+            i1 = i2 + 1;
+            i2 = frameDefinition.indexOf('\n', i1);
+            sourceId = Integer.parseInt(frameDefinition.substring(i1, i2));
+            i1 = i2 + 1;
+            i2 = frameDefinition.indexOf('\n', i1);
+            sourceName = frameDefinition.substring(i1, i2);
+            i1 = i2 + 1;
+            i2 = frameDefinition.indexOf('\n', i1);
+            sourcePath = frameDefinition.substring(i1, i2);
+            i1 = i2 + 1;
+            i2 = frameDefinition.indexOf('\n', i1);
+            try {
+                sourceURI = new URI(frameDefinition.substring(i1, i2));
+            } catch (URISyntaxException usex) {
+                throw new IllegalStateException("Bad URI: "+frameDefinition.substring(i1, i2), usex);
+            }
+            i1 = i2 + 1;
+            if (includeInternal) {
+                i2 = frameDefinition.indexOf('\n', i1);
+                sourceLine = Integer.parseInt(frameDefinition.substring(i1, i2));
+                i1 = i2 + 1;
+                internalFrame = Boolean.valueOf(frameDefinition.substring(i1));
+            } else {
+                sourceLine = Integer.parseInt(frameDefinition.substring(i1));
+            }
+        } catch (IndexOutOfBoundsException ioob) {
+            throw new IllegalStateException("frameDefinition='"+frameDefinition+"'", ioob);
+        }
+        this.codeRef = codeRef;
+        this.scopes = scopes;
+        this.thisObject = thisObject;
+        this.isInternal = internalFrame;
+    }
+    
+    public final JPDADebugger getDebugger() {
+        return debugger;
+    }
+    
+    public final JPDAThread getThread() {
+        return thread;
+    }
+    
+    public final int getDepth() {
+        return depth;
+    }
+    
+    public String getMethodName() {
+        return methodName;
+    }
+
+    public String getSourceLocation() {
+        return sourceLocation;
+    }
+
+    public String getDisplayName() {
+        if (!methodName.isEmpty()) {
+            return methodName + " ("+sourceLocation+")";
+        } else {
+            return sourceLocation;
+        }
+    }
+    
+    public SourcePosition getSourcePosition() {
+        Source src = Source.getExistingSource(debugger, sourceId);
+        if (src == null) {
+            src = Source.getSource(debugger, sourceId, sourceName, sourcePath, sourceURI, codeRef);
+        }
+        SourcePosition sp = new SourcePosition(debugger, sourceId, src, sourceLine);
+        return sp;
+    }
+    
+    public ObjectVariable getStackFrameInstance() {
+        return frameInstance;// also is: (ObjectVariable) stackTrace.getFields(0, Integer.MAX_VALUE)[depth - 1];
+    }
+    
+    public TruffleScope[] getScopes() {
+        if (scopes == null) {
+            scopes = TruffleAccess.createFrameScopes(debugger, /*suspendedInfo,*/ getStackFrameInstance());
+        }
+        return scopes;
+    }
+    
+    public void popToHere() {
+        if (depth > 0) {
+            boolean unwindScheduled = TruffleAccess.unwind(debugger, thread, depth - 1);
+            if (unwindScheduled) {
+                CurrentPCInfo currentPCInfo = TruffleAccess.getCurrentPCInfo(thread);
+                try {
+                    currentPCInfo.getStepCommandVar().setFromMirrorObject(-1);
+                    thread.resume();
+                } catch (InvalidObjectException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+            }
+        }
+    }
+    
+    public ObjectVariable getThis() {
+        return thisObject;
+    }
+    
+    public boolean isInternal() {
+        return isInternal;
+    }
+    
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/TruffleStackInfo.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/TruffleStackInfo.java
new file mode 100644
index 0000000000..05024620f0
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/TruffleStackInfo.java
@@ -0,0 +1,111 @@
+/*
+ * 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.frames;
+
+import com.sun.jdi.StringReference;
+import java.io.InvalidObjectException;
+import java.util.ArrayList;
+import java.util.List;
+import org.netbeans.api.debugger.jpda.Field;
+import org.netbeans.api.debugger.jpda.InvalidExpressionException;
+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.ObjectVariable;
+import org.netbeans.api.debugger.jpda.Variable;
+import org.netbeans.modules.debugger.jpda.expr.JDIVariable;
+import org.netbeans.modules.debugger.jpda.truffle.TruffleDebugManager;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author Martin
+ */
+public final class TruffleStackInfo {
+    
+    private static final String METHOD_GET_FRAMES_INFO = "getFramesInfo";       // NOI18N
+    private static final String METHOD_GET_FRAMES_INFO_SIG = "([Lcom/oracle/truffle/api/debug/DebugStackFrame;Z)[Ljava/lang/Object;";   // NOI18N
+    
+    private final JPDADebugger debugger;
+    private final JPDAThread thread;
+    private final ObjectVariable stackTrace;
+    private TruffleStackFrame[] stackFrames;
+    private boolean includedInternalFrames;
+    private boolean areInternalFrames;
+
+    public TruffleStackInfo(JPDADebugger debugger, JPDAThread thread, ObjectVariable stackTrace) {
+        this.debugger = debugger;
+        this.thread = thread;
+        this.stackTrace = stackTrace;
+    }
+
+    public TruffleStackFrame[] getStackFrames(boolean includeInternal) {
+        if (stackFrames == null || includedInternalFrames != includeInternal) {
+            stackFrames = loadStackFrames(includeInternal);
+            this.includedInternalFrames = includeInternal;
+        }
+        return stackFrames;
+    }
+    
+    public boolean hasInternalFrames() {
+        return areInternalFrames;
+    }
+        
+    private TruffleStackFrame[] loadStackFrames(boolean includeInternal) {
+        JPDAClassType debugAccessor = TruffleDebugManager.getDebugAccessorJPDAClass(debugger);
+        try {
+            Variable internalVar = debugger.createMirrorVar(includeInternal, true);
+            Variable framesVar = debugAccessor.invokeMethod(METHOD_GET_FRAMES_INFO,
+                                                            METHOD_GET_FRAMES_INFO_SIG,
+                                                            new Variable[] { stackTrace,
+                                                                             internalVar });
+            Field[] framesInfos = ((ObjectVariable) framesVar).getFields(0, Integer.MAX_VALUE);
+            String framesDesc = (String) framesInfos[0].createMirrorObject();
+            Field[] codes = ((ObjectVariable) framesInfos[1]).getFields(0, Integer.MAX_VALUE);
+            Field[] thiss = ((ObjectVariable) framesInfos[2]).getFields(0, Integer.MAX_VALUE);
+            areInternalFrames = false;
+            if (!includeInternal) {
+                areInternalFrames = (Boolean) framesInfos[3].createMirrorObject();
+            }
+            int i1 = 0;
+            int i2;
+            int depth = 1;
+            List<TruffleStackFrame> truffleFrames = new ArrayList<>();
+            while ((i2 = framesDesc.indexOf("\n\n", i1)) > 0) {
+                StringReference codeRef = (StringReference) ((JDIVariable) codes[depth-1]).getJDIValue();
+                ObjectVariable frameInstance = (ObjectVariable) stackTrace.getFields(0, Integer.MAX_VALUE)[depth - 1];
+                TruffleStackFrame tsf = new TruffleStackFrame(
+                        debugger, thread, depth, frameInstance, framesDesc.substring(i1, i2),
+                        codeRef, null, (ObjectVariable) thiss[depth-1], includeInternal);
+                truffleFrames.add(tsf);
+                if (includeInternal && tsf.isInternal()) {
+                    areInternalFrames = true;
+                }
+                i1 = i2 + 2;
+                depth++;
+            }
+            return truffleFrames.toArray(new TruffleStackFrame[truffleFrames.size()]);
+        } catch (InvalidExpressionException | NoSuchMethodException | InvalidObjectException ex) {
+            Exceptions.printStackTrace(ex);
+            return new TruffleStackFrame[] {};
+        }
+    }
+    
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/models/DebuggingTruffleActionsProvider.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/models/DebuggingTruffleActionsProvider.java
new file mode 100644
index 0000000000..18a80fbc97
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/models/DebuggingTruffleActionsProvider.java
@@ -0,0 +1,284 @@
+/*
+ * 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.frames.models;
+
+import javax.swing.Action;
+import javax.swing.SwingUtilities;
+
+import org.netbeans.modules.debugger.jpda.EditorContextBridge;
+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.frames.TruffleStackFrame;
+import org.netbeans.modules.debugger.jpda.truffle.options.TruffleOptions;
+import org.netbeans.modules.debugger.jpda.truffle.source.SourcePosition;
+
+import org.netbeans.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
+import org.netbeans.spi.debugger.ui.DebuggingView;
+import org.netbeans.spi.viewmodel.Models;
+import org.netbeans.spi.viewmodel.NodeActionsProvider;
+import org.netbeans.spi.viewmodel.NodeActionsProviderFilter;
+import org.netbeans.spi.viewmodel.UnknownTypeException;
+
+import org.openide.util.NbBundle;
+import org.openide.util.RequestProcessor;
+
+@DebuggerServiceRegistration(path="netbeans-JPDASession/DebuggingView",
+                             types=NodeActionsProviderFilter.class,
+                             position=24000)
+public class DebuggingTruffleActionsProvider implements NodeActionsProviderFilter {
+    
+    private final Action MAKE_CURRENT_ACTION;
+    private final Action GO_TO_SOURCE_ACTION;
+    private final Action POP_TO_HERE_ACTION;
+    private final Action SHOW_INTERNAL_ACTION;
+    private final Action HIDE_INTERNAL_ACTION;
+    
+    public DebuggingTruffleActionsProvider(ContextProvider lookupProvider) {
+        RequestProcessor requestProcessor = lookupProvider.lookupFirst(null, RequestProcessor.class);
+        MAKE_CURRENT_ACTION = createMAKE_CURRENT_ACTION(requestProcessor);
+        GO_TO_SOURCE_ACTION = createGO_TO_SOURCE_ACTION(requestProcessor);
+        POP_TO_HERE_ACTION = createPOP_TO_HERE_ACTION(requestProcessor);
+        SHOW_INTERNAL_ACTION = createSHOW_INTERNAL_ACTION();
+        HIDE_INTERNAL_ACTION = createHIDE_INTERNAL_ACTION();
+    }
+
+    @Override
+    public void performDefaultAction(NodeActionsProvider original, Object node) throws UnknownTypeException {
+        if (node instanceof TruffleStackFrame) {
+            TruffleStackFrame f = (TruffleStackFrame) node;
+            CurrentPCInfo currentPCInfo = TruffleAccess.getCurrentPCInfo(f.getThread());
+            if (currentPCInfo != null) {
+                currentPCInfo.setSelectedStackFrame(f);
+                goToSource(f);
+            }
+        } else {
+            original.performDefaultAction(node);
+        }
+    }
+
+    @Override
+    public Action[] getActions(NodeActionsProvider original, Object node) throws UnknownTypeException {
+        if (node instanceof TruffleStackFrame) {
+            return new Action [] {
+                MAKE_CURRENT_ACTION,
+                POP_TO_HERE_ACTION,
+                GO_TO_SOURCE_ACTION,
+            };
+        /*} else if (node instanceof DebuggingView.DVThread) {
+            Action[] actions = original.getActions(node);
+            CurrentPCInfo currentPCInfo = TruffleAccess.getCurrentPCInfo(debugger);
+            if (currentPCInfo != null && currentPCInfo.getStack().hasInternalFrames()) {
+                int n = actions.length;
+                actions = Arrays.copyOf(actions, n + 1);
+                if (TruffleOptions.isLanguageDeveloperMode()) {
+                    actions[n] = HIDE_INTERNAL_ACTION;
+                } else {
+                    actions[n] = SHOW_INTERNAL_ACTION;
+                }
+            }
+            return actions;*/
+        } else {
+            return original.getActions(node);
+        }
+    }
+    
+    private static void goToSource(final TruffleStackFrame f) {
+        final SourcePosition sourcePosition = f.getSourcePosition();
+        SwingUtilities.invokeLater (new Runnable () {
+            @Override
+            public void run () {
+                EditorContextBridge.getContext().showSource (
+                    sourcePosition.getSource().getUrl().toExternalForm(),
+                    sourcePosition.getLine(),
+                    f.getDebugger()
+                );
+            }
+        });
+    }
+    
+    @NbBundle.Messages("CTL_StackFrameAction_MakeCurrent_Label=Make Current")
+    private Action createMAKE_CURRENT_ACTION(RequestProcessor requestProcessor) {
+        return Models.createAction (
+        Bundle.CTL_StackFrameAction_MakeCurrent_Label(),
+        new LazyActionPerformer (requestProcessor) {
+            @Override
+            public boolean isEnabled (Object node) {
+                if (node instanceof TruffleStackFrame) {
+                    TruffleStackFrame f = (TruffleStackFrame) node;
+                    CurrentPCInfo currentPCInfo = TruffleAccess.getCurrentPCInfo(f.getThread());
+                    if (currentPCInfo != null) {
+                        TruffleStackFrame topFrame = currentPCInfo.getTopFrame();
+                        if (topFrame == null) {
+                            return f.getDepth() > 0;
+                        } else {
+                            return f != topFrame;
+                        }
+                    }
+                }
+                return false;
+            }
+            
+            @Override
+            public void run (Object[] nodes) {
+                if (nodes.length == 0) return ;
+                if (nodes[0] instanceof TruffleStackFrame) {
+                    TruffleStackFrame f = (TruffleStackFrame) nodes[0];
+                    CurrentPCInfo currentPCInfo = TruffleAccess.getCurrentPCInfo(f.getThread());
+                    if (currentPCInfo != null) {
+                        currentPCInfo.setSelectedStackFrame(f);
+                    }
+                    goToSource(f);
+                }
+            }
+
+        },
+        Models.MULTISELECTION_TYPE_EXACTLY_ONE
+    
+    );
+    }
+    
+    @NbBundle.Messages("CTL_StackFrameAction_GoToSource_Label=Go To Source")
+    static final Action createGO_TO_SOURCE_ACTION(final RequestProcessor requestProcessor) {
+        return Models.createAction (
+            Bundle.CTL_StackFrameAction_GoToSource_Label(),
+            new Models.ActionPerformer () {
+                @Override
+                public boolean isEnabled (Object node) {
+                    if (!(node instanceof TruffleStackFrame)) {
+                        return false;
+                    }
+                    //return isGoToSourceSupported ((TruffleStackFrame) node);
+                    return true;
+                }
+
+                @Override
+                public void perform (final Object[] nodes) {
+                    // Do not do expensive actions in AWT,
+                    // It can also block if it can not procceed for some reason
+                    requestProcessor.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            goToSource((TruffleStackFrame) nodes [0]);
+                        }
+                    });
+                }
+            },
+            Models.MULTISELECTION_TYPE_EXACTLY_ONE
+
+        );
+    }
+    
+    @NbBundle.Messages("CTL_StackFrameAction_ShowInternal_Label=Show Internal Frames")
+    static final Action createSHOW_INTERNAL_ACTION() {
+        return Models.createAction (
+            Bundle.CTL_StackFrameAction_ShowInternal_Label(),
+            new Models.ActionPerformer () {
+                @Override
+                public boolean isEnabled (Object node) {
+                    if (!(node instanceof DebuggingView.DVThread)) {
+                        return false;
+                    }
+                    return true;
+                }
+
+                @Override
+                public void perform (final Object[] nodes) {
+                    TruffleOptions.setLanguageDeveloperMode(true);
+                }
+            },
+            Models.MULTISELECTION_TYPE_EXACTLY_ONE
+        );
+    }
+
+    @NbBundle.Messages("CTL_StackFrameAction_HideInternal_Label=Hide Internal Frames")
+    static final Action createHIDE_INTERNAL_ACTION() {
+        return Models.createAction (
+            Bundle.CTL_StackFrameAction_HideInternal_Label(),
+            new Models.ActionPerformer () {
+                @Override
+                public boolean isEnabled (Object node) {
+                    if (!(node instanceof DebuggingView.DVThread)) {
+                        return false;
+                    }
+                    return true;
+                }
+
+                @Override
+                public void perform (final Object[] nodes) {
+                    TruffleOptions.setLanguageDeveloperMode(false);
+                }
+            },
+            Models.MULTISELECTION_TYPE_EXACTLY_ONE
+        );
+    }
+
+    @NbBundle.Messages("CTL_StackFrameAction_PopToHere_Label=Pop To Here")
+    private Action createPOP_TO_HERE_ACTION(RequestProcessor requestProcessor) {
+        return Models.createAction (
+            Bundle.CTL_StackFrameAction_PopToHere_Label(),
+            new Models.ActionPerformer () {
+                @Override
+                public boolean isEnabled (Object node) {
+                    if (!(node instanceof TruffleStackFrame)) {
+                        return false;
+                    }
+                    return true;
+                }
+
+                @Override
+                public void perform (final Object[] nodes) {
+                    requestProcessor.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            ((TruffleStackFrame) nodes [0]).popToHere();
+                        }
+                    });
+                }
+            },
+            Models.MULTISELECTION_TYPE_EXACTLY_ONE
+        );
+    }
+
+    static abstract class LazyActionPerformer implements Models.ActionPerformer {
+
+        private final RequestProcessor rp;
+
+        public LazyActionPerformer(RequestProcessor rp) {
+            this.rp = rp;
+        }
+
+        @Override
+        public abstract boolean isEnabled (Object node);
+
+        @Override
+        public final void perform (final Object[] nodes) {
+            rp.post(new Runnable() {
+                @Override
+                public void run() {
+                    LazyActionPerformer.this.run(nodes);
+                }
+            });
+        }
+
+        public abstract void run(Object[] nodes);
+    }
+
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/models/DebuggingTruffleNodeModel.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/models/DebuggingTruffleNodeModel.java
new file mode 100644
index 0000000000..e5819f2204
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/models/DebuggingTruffleNodeModel.java
@@ -0,0 +1,263 @@
+/*
+ * 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.frames.models;
+
+import com.sun.jdi.AbsentInformationException;
+import java.awt.datatransfer.Transferable;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.netbeans.api.debugger.jpda.CallStackFrame;
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.JPDAThread;
+import org.netbeans.api.debugger.jpda.LocalVariable;
+import org.netbeans.api.debugger.jpda.MonitorInfo;
+import org.netbeans.api.debugger.jpda.This;
+import org.netbeans.modules.debugger.jpda.truffle.Utils;
+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.frames.TruffleStackFrame;
+import org.netbeans.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
+import org.netbeans.spi.debugger.jpda.EditorContext;
+import org.netbeans.spi.viewmodel.ExtendedNodeModel;
+import org.netbeans.spi.viewmodel.ExtendedNodeModelFilter;
+import org.netbeans.spi.viewmodel.ModelEvent;
+import org.netbeans.spi.viewmodel.ModelListener;
+import org.netbeans.spi.viewmodel.NodeModel;
+import org.netbeans.spi.viewmodel.UnknownTypeException;
+
+import org.openide.util.WeakListeners;
+import org.openide.util.WeakSet;
+import org.openide.util.datatransfer.PasteType;
+
+@DebuggerServiceRegistration(path="netbeans-JPDASession/DebuggingView",
+                             types=ExtendedNodeModelFilter.class,
+                             position=23000)
+public class DebuggingTruffleNodeModel implements ExtendedNodeModelFilter {
+    
+    private final JPDADebugger debugger;
+    private final List<ModelListener> listeners = new CopyOnWriteArrayList<ModelListener>();
+    private final WeakSet<CurrentPCInfo> cpisListening = new WeakSet<CurrentPCInfo>();
+    private final CurrentInfoPropertyChangeListener cpiChL = new CurrentInfoPropertyChangeListener();
+    
+    public DebuggingTruffleNodeModel(ContextProvider lookupProvider) {
+        debugger = lookupProvider.lookupFirst(null, JPDADebugger.class);
+    }
+
+    @Override
+    public boolean canRename(ExtendedNodeModel original, Object node) throws UnknownTypeException {
+        return original.canRename(node);
+    }
+
+    @Override
+    public boolean canCopy(ExtendedNodeModel original, Object node) throws UnknownTypeException {
+        return original.canCopy(node);
+    }
+
+    @Override
+    public boolean canCut(ExtendedNodeModel original, Object node) throws UnknownTypeException {
+        return original.canCut(node);
+    }
+
+    @Override
+    public Transferable clipboardCopy(ExtendedNodeModel original, Object node) throws IOException, UnknownTypeException {
+        return original.clipboardCopy(node);
+    }
+
+    @Override
+    public Transferable clipboardCut(ExtendedNodeModel original, Object node) throws IOException, UnknownTypeException {
+        return original.clipboardCut(node);
+    }
+
+    @Override
+    public PasteType[] getPasteTypes(ExtendedNodeModel original, Object node, Transferable t) throws UnknownTypeException {
+        return original.getPasteTypes(node, t);
+    }
+
+    @Override
+    public void setName(ExtendedNodeModel original, Object node, String name) throws UnknownTypeException {
+        original.setName(node, name);
+    }
+
+    @Override
+    public String getIconBaseWithExtension(ExtendedNodeModel original, Object node) throws UnknownTypeException {
+        if (node instanceof TruffleStackFrame) {
+            return original.getIconBaseWithExtension(EmptyCallStackFrame.INSTANCE);
+        }
+        return original.getIconBaseWithExtension(node);
+    }
+
+    @Override
+    public String getDisplayName(NodeModel original, Object node) throws UnknownTypeException {
+        if (node instanceof TruffleStackFrame) {
+            TruffleStackFrame tf = (TruffleStackFrame) node;
+            String displayName = tf.getDisplayName();
+            CurrentPCInfo currentPCInfo = TruffleAccess.getCurrentPCInfo(tf.getThread());
+            if (currentPCInfo != null) {
+                synchronized (cpisListening) {
+                    if (!cpisListening.contains(currentPCInfo)) {
+                        currentPCInfo.addPropertyChangeListener(
+                                WeakListeners.propertyChange(cpiChL, currentPCInfo));
+                        cpisListening.add(currentPCInfo);
+                    }
+                }
+                TruffleStackFrame selectedStackFrame = currentPCInfo.getSelectedStackFrame();
+                if (selectedStackFrame == tf) {
+                    displayName = Utils.toHTML(displayName, true, tf.isInternal(), null);
+                } else if (tf.isInternal()) {
+                    displayName = Utils.toHTML(displayName, false, true, null);
+                }
+            }
+            return displayName;
+        } else {
+            return original.getDisplayName(node);
+        }
+    }
+
+    @Override
+    public String getIconBase(NodeModel original, Object node) throws UnknownTypeException {
+        throw new UnsupportedOperationException("Not supported.");
+    }
+
+    @Override
+    public String getShortDescription(NodeModel original, Object node) throws UnknownTypeException {
+        if (node instanceof TruffleStackFrame) {
+            return ((TruffleStackFrame) node).getDisplayName();
+        } else {
+            return original.getShortDescription(node);
+        }
+    }
+    
+    private void fireDisplayNamesChanged() {
+        ModelEvent evt = new ModelEvent.NodeChanged(this, null);
+        for (ModelListener l : listeners) {
+            l.modelChanged(evt);
+        }
+    }
+
+    @Override
+    public void addModelListener(ModelListener l) {
+        listeners.add(l);
+    }
+
+    @Override
+    public void removeModelListener(ModelListener l) {
+        listeners.remove(l);
+    }
+    
+    private class CurrentInfoPropertyChangeListener implements PropertyChangeListener {
+
+        @Override
+        public void propertyChange(PropertyChangeEvent evt) {
+            fireDisplayNamesChanged();
+        }
+        
+    }
+    
+    private static final class EmptyCallStackFrame implements CallStackFrame {
+        
+        static final CallStackFrame INSTANCE = new EmptyCallStackFrame();
+
+        @Override
+        public int getLineNumber(String struts) {
+            return 1;
+        }
+
+        @Override
+        public int getFrameDepth() {
+            return 0;
+        }
+
+        @Override
+        public EditorContext.Operation getCurrentOperation(String struts) {
+            return null;
+        }
+
+        @Override
+        public String getMethodName() {
+            return "";
+        }
+
+        @Override
+        public String getClassName() {
+            return "";
+        }
+
+        @Override
+        public String getDefaultStratum() {
+            return "";
+        }
+
+        @Override
+        public List<String> getAvailableStrata() {
+            return Collections.emptyList();
+        }
+
+        @Override
+        public String getSourceName(String struts) throws AbsentInformationException {
+            return "";
+        }
+
+        @Override
+        public String getSourcePath(String stratum) throws AbsentInformationException {
+            return "";
+        }
+
+        @Override
+        public LocalVariable[] getLocalVariables() throws AbsentInformationException {
+            return new LocalVariable[] {};
+        }
+
+        @Override
+        public This getThisVariable() {
+            return null;
+        }
+
+        @Override
+        public void makeCurrent() {
+        }
+
+        @Override
+        public boolean isObsolete() {
+            return false;
+        }
+
+        @Override
+        public void popFrame() {
+        }
+
+        @Override
+        public JPDAThread getThread() {
+            return null;
+        }
+
+        @Override
+        public List<MonitorInfo> getOwnedMonitors() {
+            return Collections.emptyList();
+        }
+        
+    }
+    
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/models/DebuggingTruffleTreeExpansionModelFilter.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/models/DebuggingTruffleTreeExpansionModelFilter.java
new file mode 100644
index 0000000000..ee606a1550
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/models/DebuggingTruffleTreeExpansionModelFilter.java
@@ -0,0 +1,164 @@
+/*
+ * 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.frames.models;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.netbeans.api.debugger.jpda.CallStackFrame;
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.JPDAThread;
+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.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
+import org.netbeans.spi.debugger.ui.DebuggingView.DVSupport;
+import org.netbeans.spi.debugger.ui.DebuggingView.DVThread;
+import org.netbeans.spi.viewmodel.ModelEvent;
+import org.netbeans.spi.viewmodel.ModelListener;
+import org.netbeans.spi.viewmodel.TreeExpansionModel;
+import org.netbeans.spi.viewmodel.TreeExpansionModelFilter;
+import org.netbeans.spi.viewmodel.UnknownTypeException;
+import org.openide.util.Exceptions;
+import org.openide.util.WeakListeners;
+import org.openide.util.WeakSet;
+
+/**
+ * Assure that the thread we stop in is automatically expanded.
+ */
+@DebuggerServiceRegistration(path="netbeans-JPDASession/"+TruffleStrataProvider.TRUFFLE_STRATUM+"/DebuggingView",
+                             types={ TreeExpansionModelFilter.class })
+public class DebuggingTruffleTreeExpansionModelFilter implements TreeExpansionModelFilter,
+                                                                 PropertyChangeListener {
+
+    private static final Reference<JPDAThread> NO_THREAD = new WeakReference<>(null);
+    private final JPDADebugger debugger;
+    private final DVSupport dvSupport;
+    private volatile Reference<JPDAThread> suspendedTruffleThread = NO_THREAD;
+    private final Set<Object> collapsedExplicitly = new WeakSet<>();
+    private final Set<ModelListener> listeners = Collections.synchronizedSet(new HashSet<ModelListener>());
+
+    public DebuggingTruffleTreeExpansionModelFilter(ContextProvider context) {
+        debugger = context.lookupFirst(null, JPDADebugger.class);
+        debugger.addPropertyChangeListener(
+                JPDADebugger.PROP_CURRENT_CALL_STACK_FRAME,
+                WeakListeners.propertyChange(this, JPDADebugger.PROP_CURRENT_CALL_STACK_FRAME, debugger));
+        dvSupport = context.lookupFirst(null, DVSupport.class);
+        currentStackFrameChanged(debugger.getCurrentCallStackFrame());
+    }
+
+    @Override
+    public boolean isExpanded(TreeExpansionModel original, Object node) throws UnknownTypeException {
+        if (node instanceof DVThread) {
+            synchronized (this) {
+                if (collapsedExplicitly.contains(node)) {
+                    return false;
+                }
+            }
+            try {
+                JPDAThread thread = (JPDAThread) node.getClass().getMethod("getKey").invoke(node);
+                if (thread == suspendedTruffleThread.get()) {
+                    return true;
+                }
+            } catch (Exception ex) {
+                Exceptions.printStackTrace(ex);
+            }
+        }
+        return original.isExpanded(node);
+    }
+
+    @Override
+    public void nodeExpanded(Object node) {
+        if (node instanceof DVThread) {
+            synchronized (this) {
+                collapsedExplicitly.remove(node);
+            }
+        }
+    }
+
+    @Override
+    public void nodeCollapsed(Object node) {
+        if (node instanceof DVThread) {
+            synchronized (this) {
+                collapsedExplicitly.add(node);
+            }
+        }
+    }
+
+    private void fireNodeExpanded(Object node) {
+        ModelListener[] ls = listeners.toArray(new ModelListener[] {});
+        if (ls.length > 0) {
+            ModelEvent event = new ModelEvent.NodeChanged(this, node,
+                    ModelEvent.NodeChanged.EXPANSION_MASK);
+            for (ModelListener ml : ls) {
+                ml.modelChanged(event);
+            }
+        }
+    }
+
+    @Override
+    public void addModelListener(ModelListener l) {
+        listeners.add(l);
+    }
+
+    @Override
+    public void removeModelListener(ModelListener l) {
+        listeners.remove(l);
+    }
+
+    @Override
+    public void propertyChange(PropertyChangeEvent evt) {
+        // JPDADebugger PROP_CURRENT_CALL_STACK_FRAME
+        CallStackFrame csf = (CallStackFrame) evt.getNewValue();
+        currentStackFrameChanged(csf);
+    }
+
+    private void currentStackFrameChanged(CallStackFrame csf) {
+        CurrentPCInfo currentPCInfo = (csf != null) ? TruffleAccess.getCurrentPCInfo(csf.getThread()) : null;
+        if (csf != null && currentPCInfo != null && csf.getThread() == currentPCInfo.getThread()) {
+            JPDAThread thread = csf.getThread();
+            suspendedTruffleThread = new WeakReference<>(thread);
+            try {
+                Object node = dvSupport.getClass().getMethod("get", JPDAThread.class).invoke(dvSupport, thread);
+                boolean explicitCollaps;
+                synchronized (this) {
+                    explicitCollaps = collapsedExplicitly.contains(node);
+                }
+                if (!explicitCollaps) {
+                    fireNodeExpanded(node);
+                }
+            } catch (IllegalAccessException | IllegalArgumentException |
+                     InvocationTargetException | NoSuchMethodException |
+                     SecurityException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+        } else {
+            suspendedTruffleThread = NO_THREAD;
+        }
+    }
+
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/models/DebuggingTruffleTreeModel.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/models/DebuggingTruffleTreeModel.java
new file mode 100644
index 0000000000..2a62257176
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/frames/models/DebuggingTruffleTreeModel.java
@@ -0,0 +1,145 @@
+/*
+ * 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.frames.models;
+
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
+
+import org.netbeans.api.debugger.jpda.CallStackFrame;
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.JPDAThread;
+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.options.TruffleOptions;
+import org.netbeans.modules.debugger.jpda.util.WeakCacheMap;
+import org.netbeans.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
+import org.netbeans.spi.debugger.ui.DebuggingView;
+import org.netbeans.spi.viewmodel.ModelEvent;
+import org.netbeans.spi.viewmodel.ModelListener;
+import org.netbeans.spi.viewmodel.TreeModel;
+import org.netbeans.spi.viewmodel.TreeModelFilter;
+import org.netbeans.spi.viewmodel.UnknownTypeException;
+
+@DebuggerServiceRegistration(path="netbeans-JPDASession/"+TruffleStrataProvider.TRUFFLE_STRATUM+"/DebuggingView",
+                             types={ TreeModelFilter.class })
+public class DebuggingTruffleTreeModel implements TreeModelFilter {
+    
+    private static final Predicate<String> PREDICATE1 = Pattern.compile("^((com|org)\\.\\p{Alpha}*\\.truffle|(com|org)\\.graalvm)\\..*$").asPredicate();
+    private static final String FILTER1 = "com.[A-z]*.truffle.";                     // NOI18N
+    private static final String FILTER2 = "com.oracle.graal.";                  // NOI18N
+    private static final String FILTER3 = "org.netbeans.modules.debugger.jpda.backend.";    // NOI18N
+    
+    private final JPDADebugger debugger;
+    private final List<ModelListener> listeners = new ArrayList<>();
+    private final PropertyChangeListener propListenerHolder;    // Not to have the listener collected
+    
+    public DebuggingTruffleTreeModel(ContextProvider lookupProvider) {
+        debugger = lookupProvider.lookupFirst(null, JPDADebugger.class);
+        propListenerHolder = propEvent -> {
+            ModelListener[] mls;
+            synchronized (listeners) {
+                mls = listeners.toArray(new ModelListener[listeners.size()]);
+            }
+            ModelEvent event = new ModelEvent.TreeChanged(TreeModel.ROOT);
+            for (ModelListener ml : mls) {
+                ml.modelChanged(event);
+            }
+        };
+        TruffleOptions.onLanguageDeveloperModeChange(propListenerHolder);
+    }
+
+    @Override
+    public Object getRoot(TreeModel original) {
+        return original.getRoot();
+    }
+
+    @Override
+    public Object[] getChildren(TreeModel original, Object parent, int from, int to) throws UnknownTypeException {
+        Object[] children = original.getChildren(parent, from, to);
+        if (parent instanceof DebuggingView.DVThread && children.length > 0) {
+            CurrentPCInfo currentPCInfo = TruffleAccess.getCurrentPCInfo(((WeakCacheMap.KeyedValue<JPDAThread>) parent).getKey());
+            if (currentPCInfo != null) {
+                boolean showInternalFrames = TruffleOptions.isLanguageDeveloperMode();
+                TruffleStackFrame[] stackFrames = currentPCInfo.getStack().getStackFrames(showInternalFrames);
+                children = filterAndAppend(children, stackFrames, currentPCInfo.getTopFrame());
+            }
+        }
+        return children;
+    }
+
+    @Override
+    public int getChildrenCount(TreeModel original, Object node) throws UnknownTypeException {
+        return Integer.MAX_VALUE;
+    }
+
+    @Override
+    public boolean isLeaf(TreeModel original, Object node) throws UnknownTypeException {
+        if (node instanceof TruffleStackFrame) {
+            return true;
+        } else {
+            return original.isLeaf(node);
+        }
+    }
+
+    @Override
+    public void addModelListener(ModelListener l) {
+        synchronized (listeners) {
+            listeners.add(l);
+        }
+    }
+
+    @Override
+    public void removeModelListener(ModelListener l) {
+        synchronized (listeners) {
+            listeners.remove(l);
+        }
+    }
+
+    private Object[] filterAndAppend(Object[] children, TruffleStackFrame[] stackFrames,
+                                     TruffleStackFrame topFrame) {
+        List<Object> newChildren = new ArrayList<>(children.length);
+        //newChildren.addAll(Arrays.asList(children));
+        for (Object ch : children) {
+            if (ch instanceof CallStackFrame) {
+                String className = ((CallStackFrame) ch).getClassName();
+                if (PREDICATE1.test(className) ||
+                    className.startsWith(FILTER2) ||
+                    className.startsWith(FILTER3)) {
+                    
+                    continue;
+                }
+            }
+            newChildren.add(ch);
+        }
+        int i = 0;
+        newChildren.add(i++, topFrame);
+        for (TruffleStackFrame tsf : stackFrames) {
+            newChildren.add(i++, tsf);
+        }
+        return newChildren.toArray();
+    }
+    
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/layer.xml b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/layer.xml
new file mode 100644
index 0000000000..a76f3e25b8
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/layer.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.2//EN" "http://www.netbeans.org/dtds/filesystem-1_2.dtd">
+<filesystem>
+    <folder name="Editors">
+        <folder name="GlyphGutterActions">
+            <file name="org-netbeans-modules-debugger-ui-actions-ToggleBreakpointAction.shadow">
+                <attr name="originalFile" stringvalue="Actions/Debug/org-netbeans-modules-debugger-ui-actions-ToggleBreakpointAction.instance"/>
+                <attr name="position" intvalue="1000"/>
+            </file>
+        </folder>
+        <folder name="text">
+            <folder name="javascript">
+                <folder name="ToolTips">
+                    <file name="org-netbeans-modules-debugger-jpda-truffle-vars-tooltip-ToolTipAnnotation.instance"/>
+                </folder>
+            </folder>
+        </folder>
+    </folder>
+
+    <folder name="Actions">
+        <folder name="Debug">
+            <file name="org-netbeans-modules-debugger-jpda-truffle-actions-PauseInScriptAction.instance">
+                <attr name="instanceClass" stringvalue="org.netbeans.modules.debugger.ui.actions.DebuggerAction"/>
+                <attr name="instanceOf" stringvalue="javax.swing.Action"/>;
+                <attr name="instanceCreate" methodvalue="org.netbeans.modules.debugger.jpda.truffle.actions.PauseInGraalScriptActionProvider.createAction"/>
+                <attr name="action" stringvalue="pauseInGraalScript"/>
+                <attr name="name" bundlevalue="org.netbeans.modules.debugger.jpda.truffle.actions.Bundle#CTL_PauseInGraalScriptAction"/>
+                <attr name="iconBase" stringvalue="org/netbeans/modules/debugger/jpda/truffle/resources/PauseInGraalScript.png"/>
+            </file>
+        </folder>
+    </folder>
+
+    <folder name="Toolbars">
+        <folder name="Debug">
+            <file name="org-netbeans-modules-debugger-jpda-truffle-actions-PauseInScriptAction.shadow">
+                <attr name="originalFile" stringvalue="Actions/Debug/org-netbeans-modules-debugger-jpda-truffle-actions-PauseInScriptAction.instance"/>
+                <attr name="position" intvalue="930"/>
+            </file>
+        </folder>
+    </folder>
+
+    <folder name="debugger">
+        <folder name="jpda">
+            <folder name="options">
+                <file name="org-netbeans-modules-debugger-jpda-truffle-options-TruffleOptionsProvider.instance">
+                    <attr name="instanceClass" stringvalue="org.netbeans.modules.debugger.jpda.truffle.options.TruffleOptionsProvider"/>
+                    <attr name="instanceOf" stringvalue="org.netbeans.modules.debugger.jpda.ui.options.StorablePanel$Provider"/>
+                </file>
+            </folder>
+        </folder>
+    </folder>
+
+    <folder name="Windows2">
+        <folder name="Components">
+            <file name="TruffleASTDebugView.settings" url="resources/TruffleASTDebugView.settings"/>
+        </folder>
+
+        <folder name="Modes">
+            <folder name="navigator">
+                <file name="TruffleASTDebugView.wstcref" url="resources/TruffleASTDebugView.wstcref">
+                    <attr name="position" intvalue="1000"/>
+                </file>
+            </folder>
+        </folder>
+        
+        <!--folder name="Groups">
+            <folder name="debugger">
+                <file name="resultsView.wstcgrp" url="groups/debugger/results.wstcgrp"/>
+            </folder>
+        </folder-->
+    </folder>
+
+</filesystem>
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/mime/LanguageResolvers.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/mime/LanguageResolvers.java
new file mode 100644
index 0000000000..057a1aa684
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/mime/LanguageResolvers.java
@@ -0,0 +1,43 @@
+/*
+ * 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.mime;
+
+import org.openide.filesystems.MIMEResolver;
+
+/**
+ * Inform the IDE of MIME types of files interpreted by Truffle/GraalVM. This is
+ * necessary for the files to be recognized for breakpoints submission, etc.
+ * based on the MIME type.
+ */
+public class LanguageResolvers {
+    
+    @MIMEResolver.ExtensionRegistration(displayName = "Simple Language", extension = { "sl", "SL" }, mimeType = "application/x-r", position = 2140000100)
+    public void SimpleLanguage() {}
+    
+    @MIMEResolver.ExtensionRegistration(displayName = "R", extension = { "r", "R" }, mimeType = "application/x-r", position = 2140000200)
+    public void R() {}
+
+    @MIMEResolver.ExtensionRegistration(displayName = "Ruby", extension = { "rb", "RB", "Rb" }, mimeType = "application/x-ruby", position = 2140000300)
+    public void Ruby() {}
+
+    @MIMEResolver.ExtensionRegistration(displayName = "Python", extension = { "py", "PY", "Py" }, mimeType = "text/x-python", position = 2140000400)
+    public void Python() {}
+
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/options/Bundle.properties b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/options/Bundle.properties
new file mode 100644
index 0000000000..0f689d1671
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/options/Bundle.properties
@@ -0,0 +1,28 @@
+# 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.
+
+# Tools Options Java Debugger category
+LBL_TruffleDebugging=Truffle Debugging
+LBL_JavaDebuggerTruffle=Java Debugger
+
+#CategoryPanelTruffle.AgentCheckBox.text=Upload Visual Debugging agent to the target application
+#CategoryPanelTruffle.uploadAgentDescriptionLabel1.text=When this option is on, debugger will always upload an agent that is necessary for visual debugging.
+#CategoryPanelTruffle.uploadAgentDescriptionLabel2.text=When turned off, visual debugging will be disabled.
+CategoryPanelTruffle.langDevModeCheckBox.text=Language Developer Mode
+CategoryPanelTruffle.langDevModeDescription.text=Activates features for Truffle language developers
+CategoryPanelTruffle.kw1=Truffle Debugger
+CategoryPanelTruffle.kw2=Language Developer
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/options/CategoryPanelTruffle.form b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/options/CategoryPanelTruffle.form
new file mode 100644
index 0000000000..4375d6dc43
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/options/CategoryPanelTruffle.form
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+
+    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.
+
+-->
+
+<Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+  </AuxValues>
+
+  <Layout>
+    <DimensionLayout dim="0">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="0" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="langDevModeCheckBox" pref="726" max="32767" attributes="0"/>
+              <EmptySpace min="-2" pref="12" max="-2" attributes="0"/>
+          </Group>
+          <Group type="102" alignment="1" attributes="0">
+              <EmptySpace min="-2" pref="29" max="-2" attributes="0"/>
+              <Component id="langDevModeDescriptionLabel" pref="1125" max="32767" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+    <DimensionLayout dim="1">
+      <Group type="103" groupAlignment="0" attributes="0">
+          <Group type="102" alignment="0" attributes="0">
+              <Component id="langDevModeCheckBox" min="-2" max="-2" attributes="0"/>
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="langDevModeDescriptionLabel" min="-2" max="-2" attributes="0"/>
+              <EmptySpace pref="260" max="32767" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Component class="javax.swing.JCheckBox" name="langDevModeCheckBox">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/debugger/jpda/truffle/options/Bundle.properties" key="CategoryPanelTruffle.langDevModeCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JLabel" name="langDevModeDescriptionLabel">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/debugger/jpda/truffle/options/Bundle.properties" key="CategoryPanelTruffle.langDevModeDescription.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+  </SubComponents>
+</Form>
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/options/CategoryPanelTruffle.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/options/CategoryPanelTruffle.java
new file mode 100644
index 0000000000..02b55d9de9
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/options/CategoryPanelTruffle.java
@@ -0,0 +1,92 @@
+/*
+ * 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.options;
+
+import org.netbeans.modules.debugger.jpda.ui.options.StorablePanel;
+import org.netbeans.spi.options.OptionsPanelController;
+
+@OptionsPanelController.Keywords(keywords={"#LBL_TruffleDebugging",
+                                           "#CategoryPanelTruffle.kw1",
+                                           "#CategoryPanelTruffle.kw2"},
+                                 location="Java", tabTitle="#LBL_JavaDebuggerTruffle")
+public class CategoryPanelTruffle extends StorablePanel {
+
+    /** Creates new form CategoryPanelTruffle */
+    public CategoryPanelTruffle() {
+        initComponents();
+    }
+
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        langDevModeCheckBox = new javax.swing.JCheckBox();
+        langDevModeDescriptionLabel = new javax.swing.JLabel();
+
+        langDevModeCheckBox.setText(org.openide.util.NbBundle.getMessage(CategoryPanelTruffle.class, "CategoryPanelTruffle.langDevModeCheckBox.text")); // NOI18N
+
+        langDevModeDescriptionLabel.setText(org.openide.util.NbBundle.getMessage(CategoryPanelTruffle.class, "CategoryPanelTruffle.langDevModeDescription.text")); // NOI18N
+
+        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+        this.setLayout(layout);
+        layout.setHorizontalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addContainerGap()
+                .addComponent(langDevModeCheckBox, javax.swing.GroupLayout.DEFAULT_SIZE, 726, Short.MAX_VALUE)
+                .addGap(12, 12, 12))
+            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+                .addGap(29, 29, 29)
+                .addComponent(langDevModeDescriptionLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 1125, Short.MAX_VALUE)
+                .addContainerGap())
+        );
+        layout.setVerticalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addComponent(langDevModeCheckBox)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(langDevModeDescriptionLabel)
+                .addContainerGap(260, Short.MAX_VALUE))
+        );
+    }// </editor-fold>//GEN-END:initComponents
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JCheckBox langDevModeCheckBox;
+    private javax.swing.JLabel langDevModeDescriptionLabel;
+    // End of variables declaration//GEN-END:variables
+
+    @Override
+    public void load() {
+        langDevModeCheckBox.setSelected(TruffleOptions.isLanguageDeveloperMode());
+    }
+
+    @Override
+    public void store() {
+        TruffleOptions.setLanguageDeveloperMode(langDevModeCheckBox.isSelected());
+    }
+
+    @Override
+    public boolean isChanged() {
+        return langDevModeCheckBox.isSelected() != TruffleOptions.isLanguageDeveloperMode();
+    }
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/options/TruffleOptions.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/options/TruffleOptions.java
new file mode 100644
index 0000000000..a1ebf4b54d
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/options/TruffleOptions.java
@@ -0,0 +1,50 @@
+/*
+ * 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.options;
+
+import java.beans.PropertyChangeListener;
+import org.netbeans.api.debugger.Properties;
+import org.openide.util.WeakListeners;
+
+public class TruffleOptions {
+    
+    private static final String PROPERTIES_TRUFFLE = "debugger.options.JPDA.truffle"; // NOI18N
+    public static final String PROPERTY_LANG_DEV_MODE = "LanguageDeveloperMode";  // NOI18N
+
+    private static final Properties PROPERTIES = Properties.getDefault().getProperties(PROPERTIES_TRUFFLE);
+    
+    private TruffleOptions() {}
+    
+    private static boolean isLanguageDeveloperModeDefault() {
+        return false;
+    }
+    
+    public static boolean isLanguageDeveloperMode() {
+        return PROPERTIES.getBoolean(PROPERTY_LANG_DEV_MODE, isLanguageDeveloperModeDefault());
+    }
+    
+    public static void setLanguageDeveloperMode(boolean ldm) {
+        PROPERTIES.setBoolean(PROPERTY_LANG_DEV_MODE, ldm);
+    }
+
+    public static void onLanguageDeveloperModeChange(PropertyChangeListener chl) {
+        PROPERTIES.addPropertyChangeListener(WeakListeners.propertyChange(chl, PROPERTIES));
+    }
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/options/TruffleOptionsProvider.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/options/TruffleOptionsProvider.java
new file mode 100644
index 0000000000..d7ad4c88df
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/options/TruffleOptionsProvider.java
@@ -0,0 +1,37 @@
+/*
+ * 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.options;
+
+import org.netbeans.modules.debugger.jpda.ui.options.StorablePanel;
+import org.openide.util.NbBundle;
+
+public class TruffleOptionsProvider implements StorablePanel.Provider {
+
+    @Override
+    public String getPanelName() {
+        return NbBundle.getMessage(TruffleOptionsProvider.class, "LBL_TruffleDebugging");
+    }
+
+    @Override
+    public StorablePanel getPanel() {
+        return new CategoryPanelTruffle();
+    }
+    
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/resources/PauseInGraalScript.png b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/resources/PauseInGraalScript.png
new file mode 100644
index 0000000000..a106878522
Binary files /dev/null and b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/resources/PauseInGraalScript.png differ
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/resources/PauseInGraalScript24.png b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/resources/PauseInGraalScript24.png
new file mode 100644
index 0000000000..dfa37cf31b
Binary files /dev/null and b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/resources/PauseInGraalScript24.png differ
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/resources/TruffleASTDebugView.settings b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/resources/TruffleASTDebugView.settings
new file mode 100644
index 0000000000..a722632315
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/resources/TruffleASTDebugView.settings
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<!--
+
+    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.
+
+-->
+<!DOCTYPE settings PUBLIC "-//NetBeans//DTD Session settings 1.0//EN" "http://www.netbeans.org/dtds/sessionsettings-1_0.dtd">
+<settings version="1.0">
+    <module name="org.netbeans.modules.debugger.jpda.truffle/1" spec="0.15"/>
+    <instanceof class="org.openide.explorer.ExplorerManager$Provider"/>
+    <instanceof class="org.openide.windows.TopComponent"/>
+    <instance class="org.netbeans.modules.debugger.jpda.truffle.ast.view.ASTView" method="getASTView"/>
+</settings>
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/resources/TruffleASTDebugView.wstcref b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/resources/TruffleASTDebugView.wstcref
new file mode 100644
index 0000000000..0ce217eb84
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/resources/TruffleASTDebugView.wstcref
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+
+    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.
+
+-->
+
+<!DOCTYPE tc-ref PUBLIC
+          "-//NetBeans//DTD Top Component in Mode Properties 2.0//EN"
+          "http://www.netbeans.org/dtds/tc-ref2_0.dtd">
+
+<tc-ref version="2.0">
+    <tc-id id="TruffleASTDebugView" />
+    <state opened="false" />
+</tc-ref>
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
new file mode 100644
index 0000000000..40ff60b2eb
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/Source.java
@@ -0,0 +1,181 @@
+/*
+ * 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 com.sun.jdi.StringReference;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.ObjectCollectedExceptionWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.StringReferenceWrapper;
+import org.netbeans.modules.debugger.jpda.jdi.VMDisconnectedExceptionWrapper;
+
+import org.openide.filesystems.FileObject;
+import org.openide.util.Exceptions;
+
+/**
+ * Virtual guest language script source.
+ */
+public final class Source {
+    
+    public static final String URL_PROTOCOL = "truffle-scripts"; // NOI18N
+    static final String ATTR_URI = "com.oracle.truffle InternalURI"; // NOI18N
+    
+    private static final Map<JPDADebugger, Map<Long, Source>> KNOWN_SOURCES = new WeakHashMap<>();
+
+    private final StringReference codeRef;
+    private final String name;
+    private final URI uri;          // The original source URI
+    private final URL url;          // The source
+    private final long hash;
+    private String content;
+    
+    private Source(String name, URI uri, long hash, StringReference codeRef) {
+        this.name = name;
+        this.codeRef = codeRef;
+        URL url = null;
+        if (uri == null || !"file".equalsIgnoreCase(uri.getScheme())) {
+            try {
+                url = SourceFilesCache.getDefault().getSourceFile(name, hash, uri, getContent());
+            } catch (IOException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+        }
+        if (url == null) {
+            try {
+                url = uri.toURL();
+            } catch (MalformedURLException muex) {
+                Exceptions.printStackTrace(muex);
+            }
+        }
+        this.url = url;
+        this.uri = uri;
+        this.hash = hash;
+    }
+    
+    public static Source getExistingSource(JPDADebugger debugger, long id) {
+        synchronized (KNOWN_SOURCES) {
+            Map<Long, Source> dbgSources = KNOWN_SOURCES.get(debugger);
+            if (dbgSources != null) {
+                Source src = dbgSources.get(id);
+                if (src != null) {
+                    return src;
+                }
+            }
+        }
+        return null;
+    }
+    
+    /**
+     * Find an existing Source instance for the given FileObject.
+     * Currently, this method returns sources only for non-file sources.
+     * @param fo
+     * @return a Source, or <code>null</code>.
+     *
+    public static Source get(JPDADebugger debugger, FileObject fo) {
+        URI uri = fo.toURI();
+        if (!URL_PROTOCOL.equals(uri.getScheme())) {
+            return null;
+        }
+        String path = uri.getPath();
+        int hashEnd = path.indexOf('/');
+        String hashStr = path.substring(0, hashEnd);
+        long id = Long.parseUnsignedLong(hashStr, 16);
+        return getExistingSource(debugger, id);
+    }*/
+    
+    public static URI getTruffleInternalURI(FileObject fo) {
+        return (URI) fo.getAttribute(ATTR_URI);
+    }
+    
+    public static Source getSource(JPDADebugger debugger, long id,
+                                   String name,
+                                   String path,
+                                   URI uri,
+                                   StringReference codeRef) {
+        synchronized (KNOWN_SOURCES) {
+            Map<Long, Source> dbgSources = KNOWN_SOURCES.get(debugger);
+            if (dbgSources != null) {
+                Source src = dbgSources.get(id);
+                if (src != null) {
+                    return src;
+                }
+            }
+        }
+        return getTheSource(debugger, id, name, path, uri, codeRef);
+    }
+    
+    private static Source getTheSource(JPDADebugger debugger, long id,
+                                       String name,
+                                       String path,
+                                       URI uri,
+                                       StringReference codeRef) {
+        
+        Source src = new Source(name, uri, id, codeRef);
+        synchronized (KNOWN_SOURCES) {
+            Map<Long, Source> dbgSources = KNOWN_SOURCES.get(debugger);
+            if (dbgSources == null) {
+                dbgSources = new HashMap<>();
+                KNOWN_SOURCES.put(debugger, dbgSources);
+            }
+            dbgSources.put(id, src);
+        }
+        return src;
+    }
+    
+    public String getName() {
+        return name;
+    }
+
+    public URL getUrl() {
+        return url;
+    }
+    
+    public URI getURI() {
+        return uri;
+    }
+    
+    public long getHash() {
+        return hash;
+    }
+
+    public String getContent() {
+        synchronized (this) {
+            if (content == null) {
+                try {
+                    content = StringReferenceWrapper.value(codeRef);
+                } catch (InternalExceptionWrapper |
+                         VMDisconnectedExceptionWrapper |
+                         ObjectCollectedExceptionWrapper ex) {
+                    content = ex.getLocalizedMessage();
+                }
+            }
+            return content;
+        }
+    }
+    
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/SourceConnection.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/SourceConnection.java
new file mode 100644
index 0000000000..9fe827c611
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/SourceConnection.java
@@ -0,0 +1,120 @@
+/*
+ * 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.FileNotFoundException;
+import java.io.FilePermission;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.UnknownServiceException;
+import java.security.Permission;
+
+import org.openide.filesystems.FileObject;
+
+final class SourceConnection extends URLConnection {
+    
+    /** FileObject that we want to connect to. */
+    private FileObject fo;
+    /** 1 URLConnection == 1 InputSteam*/
+    private InputStream iStream = null;
+
+    
+    SourceConnection(URL url) {
+        super(url);
+    }
+    
+    public @Override synchronized void connect() throws IOException {
+        if (fo == null) {
+            fo = SourceURLMapper.find(url);
+        }
+        if (fo == null) {
+            throw new FileNotFoundException(url.toString());
+        }
+    }
+    
+    /*
+     * @return InputStream of the given FileObject.
+     */
+    public @Override InputStream getInputStream() throws IOException, UnknownServiceException {
+        connect();
+
+        if (iStream == null) {
+            if (fo.isFolder()) {
+                throw new FileNotFoundException("Can not read from a folder.");
+            } else {
+                iStream = fo.getInputStream();
+            }
+        }
+
+        return iStream;
+    }
+
+    /*
+     * @return length of FileObject.
+     */
+    public @Override int getContentLength() {
+        try {
+            connect();
+
+            return (int) fo.getSize();
+        } catch (IOException ex) {
+            return 0;
+        }
+    }
+
+    /** Get a header field (currently, content type only).
+     * @param name the header name. Only <code>content-type</code> is guaranteed to be present.
+     * @return the value (i.e., MIME type)
+     */
+    public @Override String getHeaderField(String name) {
+        if (name.equalsIgnoreCase("content-type")) { // NOI18N
+
+            try {
+                connect();
+
+                if (fo.isData()) {
+                    return fo.getMIMEType();
+                }
+            } catch (IOException e) {
+            }
+        }
+
+        return super.getHeaderField(name);
+    }
+
+    public @Override long getHeaderFieldDate(String name, long Default) {
+        if (name.equalsIgnoreCase("last-modified")) { // NOI18N
+            try {
+                connect();
+                return fo.lastModified().getTime();
+            } catch (IOException e) {
+            }
+        }
+        return super.getHeaderFieldDate(name, Default);
+    }
+
+    public @Override Permission getPermission() throws IOException {
+        // fallback
+        return new FilePermission("<<ALL FILES>>", "read"); // NOI18N
+    }
+
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/SourceFS.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/SourceFS.java
new file mode 100644
index 0000000000..afeaeca0f1
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/SourceFS.java
@@ -0,0 +1,346 @@
+/*
+ * 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.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import org.openide.filesystems.AbstractFileSystem;
+import org.openide.filesystems.FileObject;
+import org.openide.util.NbBundle;
+import org.openide.util.io.ReaderInputStream;
+
+/**
+ * File system of virtual guest language sources.
+ */
+@NbBundle.Messages("SourceFSDisplayName=GraalVM Script Sources")
+final class SourceFS extends AbstractFileSystem {
+    
+    private static final AtomicLong COUNT = new AtomicLong();
+    private final long id = COUNT.incrementAndGet();
+    
+    private final Map<String, Item> items;
+
+    SourceFS() {
+        this.items = new LinkedHashMap<>();
+        list = new SourceList();
+        info = new SourceInfo();
+        attr = new SourceAttributes();
+    }
+    
+    @Override
+    public String getDisplayName() {
+        return Bundle.SourceFSDisplayName();
+    }
+
+    @Override
+    public boolean isReadOnly() {
+        return true;
+    }
+
+    public List getList() {
+        return list;
+    }
+    
+    public long getID() {
+        return id;
+    }
+    
+    public FileObject createFile(String path, String content) {
+        int i = path.lastIndexOf('/');
+        String name = (i >= 0) ? path.substring(i + 1) : path;
+        Item item = new Item(name, content);
+        FileObject parent;
+        synchronized (items) {
+            items.put(path, item);
+            parent = getExistingParent(path);
+        }
+        parent.refresh(true);
+        return findResource(path);
+    }
+    
+    private FileObject getExistingParent(String path) {
+        FileObject parent = findResource(path);
+        if (parent != null) {
+            return parent.getParent();
+        }
+        while (parent == null && !path.isEmpty()) {
+            int i = path.lastIndexOf('/');
+            if (i > 0) {
+                path = path.substring(0, i);
+            } else {
+                break;
+            }
+            parent = findResource(path);
+        }
+        if (parent == null) {
+            parent = getRoot();
+        }
+        return parent;
+    }
+    
+    private Item getItem(String path) {
+        synchronized (items) {
+            return items.get(path);
+        }
+    }
+
+
+    private final class SourceList implements List, ChangeListener {
+        
+        SourceList() {
+        }
+
+        @Override
+        public String[] children(String f) {
+            while (f.startsWith("/")) {
+                f = f.substring(1);
+            }
+            if (!f.isEmpty() && !f.endsWith("/")) {
+                f = f + '/';
+            }
+            java.util.List<String> children = new ArrayList<>();
+            int fl = f.length();
+//            if (fl > 0) {
+//                fl++; // The slash
+//            }
+            synchronized (items) {
+                for (String name : items.keySet()) {
+                    if (name.startsWith(f)) {
+                        int end = name.indexOf('/', fl);
+                        String ch;
+                        if (end > 0) {
+                            ch = name.substring(fl, end);
+                        } else {
+                            ch = name.substring(fl);
+                        }
+                        children.add(ch);
+                    }
+                }
+            }
+            return children.toArray(new String[]{});
+        }
+
+        @Override
+        public void stateChanged(ChangeEvent e) {
+            refreshResource("", false); //NOI18N
+        }
+        
+    }
+    
+    private class SourceInfo implements Info {
+        
+        SourceInfo() {}
+        
+        @Override
+        public Date lastModified(String name) {
+            Item item = getItem(name);
+            if (item != null) {
+                return item.date;
+            } else {
+                return new Date(0);
+            }
+        }
+
+        @Override
+        public boolean folder(String name) {
+            if (name.isEmpty()) {
+                return true;     // The root is folder
+            }
+            synchronized (items) {
+                for (String namePath : items.keySet()) {
+                    if (namePath.startsWith(name)) {
+                        return name.length() < namePath.length();
+                    }
+                }
+            }
+            return false;   // Unknown
+        }
+
+        @Override
+        public boolean readOnly(String name) {
+            return true;
+        }
+
+        @Override
+        public String mimeType(String name) {
+            return null;
+        }
+
+        @Override
+        public long size(String name) {
+            Item item = getItem(name);
+            if (item != null) {
+                return item.getSize();
+            } else {
+                return 0;
+            }
+        }
+
+        @Override
+        public InputStream inputStream(String name) throws FileNotFoundException {
+            Item item = getItem(name);
+            if (item != null) {
+                return item.getInputStream();
+            } else {
+                throw new FileNotFoundException("Did not find '"+name+"'"); //NOI18N
+            }
+        }
+
+        @Override
+        public OutputStream outputStream(String name) throws IOException {
+            throw new IOException("Can not write to source files"); //NOI18N
+        }
+
+        @Override
+        public void lock(String name) throws IOException {
+            throw new IOException("Can not write to source files"); //NOI18N
+        }
+
+        @Override
+        public void unlock(String name) {
+        }
+
+        @Override
+        public void markUnimportant(String name) {
+        }
+    }
+
+    private class SourceAttributes implements Attr {
+
+        SourceAttributes() {
+        }
+
+        @Override
+        public Object readAttribute(String name, String attrName) {
+            if ("java.io.File".equals(attrName)) {      // NOI18N
+                return null;
+            }
+            Item item = getItem(name);
+            if (item != null) {
+                return item.getAttribute(attrName);
+            } else {
+                return null;
+            }
+        }
+
+        @Override
+        public void writeAttribute(String name, String attrName, Object value) throws IOException {
+            Item item = getItem(name);
+            if (item != null) {
+                item.setAttribute(attrName, value);
+            } else {
+                throw new IOException("Did not find '"+name+"'"); //NOI18N
+            }
+        }
+
+        @Override
+        public Enumeration<String> attributes(String name) {
+            Item item = getItem(name);
+            if (item != null) {
+                return item.getAttributes();
+            } else {
+                return Collections.emptyEnumeration();
+            }
+        }
+
+        @Override
+        public void renameAttributes(String oldName, String newName) {
+            throw new UnsupportedOperationException("Not supported."); //NOI18N
+        }
+
+        @Override
+        public void deleteAttributes(String name) {
+            Item item = getItem(name);
+            if (item != null) {
+                item.deleteAttributes();
+            }
+        }
+    }
+
+    
+    private static class Item {
+        
+        public final String name;
+        public final String content;
+        public final Date date;
+        private final Map<String, Object> attrs = new HashMap<>();
+        
+        public Item(String name, String content) {
+            this.name = name;
+            this.content = content;
+            this.date = new Date();
+        }
+
+        private long getSize() {
+            if (content != null) {
+                return content.length();
+            } else {
+                return 0l;
+            }
+        }
+
+        private InputStream getInputStream() throws FileNotFoundException {
+            try {
+                return new ReaderInputStream(new StringReader(content));
+            } catch (IOException ex) {
+                throw new FileNotFoundException(ex.getLocalizedMessage());
+            }
+        }
+
+        public Object getAttribute(String attrName) {
+            synchronized (attrs) {
+                return attrs.get(attrName);
+            }
+        }
+
+        public void setAttribute(String attrName, Object value) {
+            synchronized (attrs) {
+                attrs.put(attrName, value);
+            }
+        }
+
+        public Enumeration<String> getAttributes() {
+            synchronized (attrs) {
+                return Collections.enumeration(attrs.keySet());
+            }
+        }
+
+        public void deleteAttributes() {
+            synchronized (attrs) {
+                attrs.clear();
+            }
+        }
+        
+    }
+    
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/SourceFilesCache.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/SourceFilesCache.java
new file mode 100644
index 0000000000..42db88974f
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/SourceFilesCache.java
@@ -0,0 +1,55 @@
+/*
+ * 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.URI;
+import java.net.URL;
+
+import org.openide.filesystems.FileObject;
+
+final class SourceFilesCache {
+    
+    private static final SourceFilesCache DEFAULT = new SourceFilesCache();
+    
+    private final SourceFS fs;
+    
+    private SourceFilesCache() {
+        fs = new SourceFS();
+    }
+    
+    public static SourceFilesCache getDefault() {
+        return DEFAULT;
+    }
+    
+    public URL getSourceFile(String name, long hash, URI uri, String content) throws IOException {
+        String path = Long.toHexString(hash) + '/' + name;
+        FileObject fo = fs.findResource(path);
+        if (fo == null) {
+            fo = fs.createFile(path, content);
+            if (fo == null) {
+                throw new IllegalArgumentException("Not able to create file with name '"+name+"'. Path = "+path);
+            }
+            fo.setAttribute(Source.ATTR_URI, uri);
+        }
+        return fo.toURL();
+    }
+
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/SourcePosition.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/SourcePosition.java
new file mode 100644
index 0000000000..8a553a0c08
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/SourcePosition.java
@@ -0,0 +1,47 @@
+/*
+ * 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 org.netbeans.api.debugger.jpda.JPDADebugger;
+
+/**
+ * A script source position.
+ */
+public class SourcePosition {
+
+    private final long id;
+    private final Source src;
+    private final int line;
+    
+    public SourcePosition(JPDADebugger debugger, long id, Source src, int line) {
+        this.id = id;
+        this.src = src;
+        this.line = line;
+    }
+
+    public Source getSource() {
+        return src;
+    }
+    
+    public int getLine() {
+        return line;
+    }
+
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/SourceURLMapper.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/SourceURLMapper.java
new file mode 100644
index 0000000000..20e59d17c5
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/source/SourceURLMapper.java
@@ -0,0 +1,217 @@
+/*
+ * 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.io.UnsupportedEncodingException;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileStateInvalidException;
+import org.openide.filesystems.FileSystem;
+import org.openide.filesystems.URLMapper;
+import org.openide.util.URLStreamHandlerRegistration;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ * Support for URLs of the form js_sources://fs&lt;ID&gt;/folder/file
+ */
+@ServiceProvider(service=URLMapper.class)
+public final class SourceURLMapper extends URLMapper {
+    
+    private static final Map<Long,Reference<FileSystem>> filesystems = new HashMap<>(); // i.e. a sparse array by id
+    
+    public @Override URL getURL(FileObject fo, int type) {
+        if (type != URLMapper.INTERNAL) {
+            return null;
+        }
+        try {
+            FileSystem fs = fo.getFileSystem();
+            if (fs instanceof SourceFS) {
+                String path = fo.getPath();
+                if (fo.isFolder() && !fo.isRoot()) {
+                    path += '/';
+                }
+                return url((SourceFS) fs, path);
+            }
+        } catch (FileStateInvalidException x) {
+            // ignore
+        }
+        return null;
+    }
+    
+    // keep as separate method to avoid linking Handler until needed
+    private static synchronized URL url(SourceFS fs, String path) {
+        synchronized (filesystems) {
+            Reference<FileSystem> r = filesystems.get(fs.getID());
+            if (r == null || r.get() == null) {
+                r = new WeakReference<FileSystem>(fs);
+                filesystems.put(fs.getID(), r);
+            }
+        }
+        try {
+            return new URL(Source.URL_PROTOCOL, "fs" + fs.getID(), -1, "/" + percentEncode(path), new SourceURLHandler());
+        } catch (MalformedURLException x) {
+            throw new AssertionError(x);
+        }
+    }
+    
+    private static final Pattern HOST = Pattern.compile("fs(\\d+)"); // NOI18N
+    static FileObject find(URL url) {
+        if (!Source.URL_PROTOCOL.equals(url.getProtocol())) {
+            return null;
+        }
+        Matcher m = HOST.matcher(url.getHost());
+        if (!m.matches()) {
+            return null;
+        }
+        Reference<FileSystem> r;
+        synchronized (filesystems) {
+            r = filesystems.get(Long.parseLong(m.group(1)));
+        }
+        if (r == null) {
+            return null;
+        }
+        FileSystem fs = r.get();
+        if (fs == null) {
+            return null;
+        }
+        String path = url.getPath().substring(1);
+        path = percentDecode(path);
+        return fs.findResource(path);
+    }
+    
+    public @Override FileObject[] getFileObjects(URL url) {
+        FileObject f = find(url);
+        return f != null ? new FileObject[] {f} : null;
+    }
+    
+    public static String percentEncode(String text) {
+        StringBuilder encoded = null;
+        int li = 0;
+        for (int i = 0; i < text.length(); i++) {
+            char c = text.charAt(i);
+            int v = c;
+            if (47 <= v && v <= 57 || // slash and 0-9
+                'A' <= v && v <= 'Z' ||
+                'a' <= v && v <= 'z' ||
+                v == '.') {
+                
+                continue;
+            }
+            String e = encode(c);
+            if (encoded == null) {
+                encoded = new StringBuilder();
+            }
+            encoded.append(text.substring(li, i));
+            encoded.append(e);
+            li = i + 1;
+        }
+        if (encoded != null) {
+            if (li < text.length()) {
+                encoded.append(text.substring(li));
+            }
+            return encoded.toString();
+        } else {
+            return text;
+        }
+    }
+    
+    private static String encode(char c) {
+        String s = new String(new char[] { c });
+        byte[] bytes;
+        try {
+            bytes = s.getBytes("utf-8");
+        } catch (UnsupportedEncodingException ex) {
+            bytes = s.getBytes();
+        }
+        StringBuilder sb = new StringBuilder();
+        for (byte b : bytes) {
+            sb.append('%');
+            String hs = Integer.toHexString(b & 0xFF);
+            if (hs.length() == 1) {
+                sb.append('0');
+            }
+            sb.append(hs);
+        }
+        return sb.toString();
+    }
+    
+    public static String percentDecode(String text) {
+        int i = text.indexOf('%');
+        if (i < 0) {
+            return text;
+        }
+        StringBuilder decoded = new StringBuilder();
+        int li = 0;
+        while (i >= 0) {
+            decoded.append(text.substring(li, i));
+            List<Byte> bytes = new ArrayList<>();
+            while (text.length() > (i + 2) && text.charAt(i) == '%') {
+                int v = Integer.parseInt(text.substring(i+1, i+3), 16);
+                bytes.add(new Byte((byte) (v & 0xFF)));
+                i += 3;
+            }
+            byte[] byteArray = new byte[bytes.size()];
+            for (int bi = 0; bi < byteArray.length; bi++) {
+                byteArray[bi] = bytes.get(bi);
+            }
+            String s;
+            try {
+                s = new String(byteArray, "utf-8");
+            } catch (UnsupportedEncodingException ex) {
+                s = new String(byteArray);
+            }
+            decoded.append(s);
+            if (i < text.length() && text.charAt(i) == '%') {
+                // an extra %
+                decoded.append('%');
+                i++;
+            }
+            li = i;
+            i = text.indexOf('%', li);
+        }
+        if (li < text.length()) {
+            decoded.append(text.substring(li));
+        }
+        return decoded.toString();
+    }
+    
+
+    @URLStreamHandlerRegistration(protocol=Source.URL_PROTOCOL)
+    public static final class SourceURLHandler extends URLStreamHandler {
+        
+        protected @Override URLConnection openConnection(URL u) throws IOException {
+            
+            return new SourceConnection(u);
+        }
+    }
+}
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/TruffleEvaluator.java
new file mode 100644
index 0000000000..f62ddc82f3
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleEvaluator.java
@@ -0,0 +1,65 @@
+/*
+ * 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.vars;
+
+import org.netbeans.api.debugger.jpda.CallStackFrame;
+import org.netbeans.api.debugger.jpda.InvalidExpressionException;
+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.access.TruffleEval;
+import org.netbeans.modules.debugger.jpda.truffle.access.TruffleStrataProvider;
+import org.netbeans.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.jpda.Evaluator;
+
+@Evaluator.Registration(language=TruffleStrataProvider.TRUFFLE_STRATUM)
+public class TruffleEvaluator implements Evaluator<TruffleExpression> {
+
+    private final JPDADebugger debugger;
+
+    public TruffleEvaluator (ContextProvider lookupProvider) {
+        debugger = lookupProvider.lookupFirst (null, JPDADebugger.class);
+    }
+
+    @Override
+    public Result evaluate(Expression<TruffleExpression> expression, Context context) throws InvalidExpressionException {
+        ObjectVariable contextVariable = context.getContextVariable();
+        if (contextVariable != null) {
+            // String value of the context variable is 
+            if ("toString()".equals(expression.getExpression())) {              // NOI18N
+                //return new Result(DebuggerSupport.getVarStringValueAsVar(debugger, contextVariable));
+                return new Result(contextVariable); // TODO
+            }
+        }
+        TruffleExpression expr = expression.getPreprocessedObject();
+        if (expr == null) {
+            expr = TruffleExpression.parse(expression.getExpression());
+            expression.setPreprocessedObject(expr);
+        }
+        Variable ret = evaluateIn(expr, context.getCallStackFrame(), contextVariable);
+        return new Result(ret);
+    }
+
+    private Variable evaluateIn(TruffleExpression expr, CallStackFrame callStackFrame, ObjectVariable contextVar) throws InvalidExpressionException {
+        //return DebuggerSupport.evaluate(debugger, callStackFrame, expr.getExpression(), contextVar);
+        return TruffleEval.evaluate(debugger, expr.getExpression());
+    }
+    
+}
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/TruffleExpression.java
new file mode 100644
index 0000000000..62e9c8a7ad
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleExpression.java
@@ -0,0 +1,37 @@
+/*
+ * 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.vars;
+
+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 String getExpression() {
+        return expr;
+    }
+}
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/TruffleScope.java
new file mode 100644
index 0000000000..42370ce1a5
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleScope.java
@@ -0,0 +1,88 @@
+/*
+ * 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.vars;
+
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.ObjectVariable;
+import org.netbeans.modules.debugger.jpda.truffle.access.TruffleAccess;
+
+/**
+ * Representation of DebugScope.
+ */
+public final class TruffleScope {
+
+    private final JPDADebugger debugger;
+    private final String name;
+    private final boolean function;
+    private final ObjectVariable debugScope;
+    private TruffleVariable[] arguments;
+    private TruffleVariable[] variables;
+
+    public TruffleScope(String name, boolean function, TruffleVariable[] arguments, TruffleVariable[] variables) {
+        this.name = name;
+        this.function = function;
+        this.arguments = arguments;
+        this.variables = variables;
+        this.debugger = null;
+        this.debugScope = null;
+    }
+
+    public TruffleScope(String name, boolean function, boolean hasArgs, boolean hasVars, JPDADebugger debugger, ObjectVariable debugScope) {
+        this.name = name;
+        this.function = function;
+        if (!hasArgs) {
+            arguments = new TruffleVariable[] {};
+        }
+        if (!hasVars) {
+            variables = new TruffleVariable[] {};
+        }
+        this.debugger = debugger;
+        this.debugScope = debugScope;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public boolean isFunction() {
+        return function;
+    }
+
+    public synchronized TruffleVariable[] getArguments() {
+        if (arguments == null) {
+            loadArgsAndVars();
+        }
+        return arguments;
+    }
+
+    public synchronized TruffleVariable[] getVariables() {
+        if (variables == null) {
+            loadArgsAndVars();
+        }
+        return variables;
+    }
+
+    private void loadArgsAndVars() {
+        assert Thread.holdsLock(this);
+        TruffleVariable[][] argsAndVars = TruffleAccess.getScopeArgsAndVars(debugger, debugScope);
+        arguments = argsAndVars[0];
+        variables = argsAndVars[1];
+    }
+}
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/TruffleStackVariable.java
new file mode 100644
index 0000000000..ce5e8fd710
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleStackVariable.java
@@ -0,0 +1,132 @@
+/*
+ * 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.vars;
+
+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.source.SourcePosition;
+
+public class TruffleStackVariable implements TruffleVariable {
+    
+    private final JPDADebugger debugger;
+    private final String name;
+    private String type;
+    private final boolean readable;
+    private final boolean writable;
+    private final boolean internal;
+    private String valueStr;
+    private Supplier<SourcePosition> valueSourceSupp;
+    private Supplier<SourcePosition> typeSourceSupp;
+    private SourcePosition valueSource;
+    private SourcePosition typeSource;
+    private ObjectVariable guestObj;
+    private boolean leaf;
+    
+    public TruffleStackVariable(JPDADebugger debugger, String name, String type,
+                                boolean readable, boolean writable, boolean internal,
+                                 String valueStr, Supplier<SourcePosition> valueSource,
+                                Supplier<SourcePosition> typeSource,
+                                ObjectVariable truffleObj) {
+        this.debugger = debugger;
+        this.name = name;
+        this.type = type;
+        this.readable = readable;
+        this.writable = writable;
+        this.internal = internal;
+        this.valueStr = valueStr;
+        this.valueSourceSupp = valueSource;
+        this.typeSourceSupp = typeSource;
+        this.guestObj = truffleObj;
+        this.leaf = TruffleVariableImpl.isLeaf(truffleObj);
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String getType() {
+        return type;
+    }
+
+    @Override
+    public boolean isReadable() {
+        return readable;
+    }
+
+    @Override
+    public boolean isWritable() {
+        return writable;
+    }
+
+    @Override
+    public boolean isInternal() {
+        return internal;
+    }
+    
+    @Override
+    public Object getValue() {
+        return valueStr;
+    }
+
+    @Override
+    public ObjectVariable setValue(JPDADebugger debugger, String newExpression) {
+        ObjectVariable newGuestObject = TruffleVariableImpl.setValue(debugger, guestObj, newExpression);
+        if (newGuestObject != null) {
+            this.guestObj = newGuestObject;
+            TruffleVariable newVar = TruffleVariableImpl.get(newGuestObject);
+            this.type = newVar.getType();
+            this.valueStr = newVar.getValue().toString();
+            this.valueSource = this.typeSource = null;
+            this.valueSourceSupp = () -> newVar.getValueSource();
+            this.typeSourceSupp = () -> newVar.getTypeSource();
+            this.leaf = TruffleVariableImpl.isLeaf(guestObj);
+        }
+        return newGuestObject;
+    }
+
+    @Override
+    public synchronized SourcePosition getValueSource() {
+        if (valueSource == null) {
+            valueSource = valueSourceSupp.get();
+        }
+        return valueSource;
+    }
+
+    @Override
+    public synchronized SourcePosition getTypeSource() {
+        if (typeSource == null) {
+            typeSource = typeSourceSupp.get();
+        }
+        return typeSource;
+    }
+    
+    @Override
+    public boolean isLeaf() {
+        return leaf;
+    }
+    
+    @Override
+    public Object[] getChildren() {
+        return TruffleVariableImpl.getChildren(guestObj);
+    }
+}
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
new file mode 100644
index 0000000000..9b5827996c
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleVariable.java
@@ -0,0 +1,52 @@
+/*
+ * 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.vars;
+
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.ObjectVariable;
+import org.netbeans.modules.debugger.jpda.truffle.source.SourcePosition;
+
+/**
+ * Representation of <code>DebugValue</code>.
+ */
+public interface TruffleVariable {
+    
+    String getName();
+    
+    String getType();
+
+    boolean isReadable();
+    
+    boolean isWritable();
+    
+    boolean isInternal();
+    
+    Object getValue();
+    
+    SourcePosition getValueSource();
+    
+    SourcePosition getTypeSource();
+    
+    boolean isLeaf();
+    
+    Object[] getChildren();
+
+    ObjectVariable setValue(JPDADebugger debugger, String newExpression);
+}
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/TruffleVariableImpl.java
new file mode 100644
index 0000000000..f9a45de53f
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/TruffleVariableImpl.java
@@ -0,0 +1,261 @@
+/*
+ * 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.vars;
+
+import java.io.InvalidObjectException;
+import org.netbeans.api.debugger.jpda.Field;
+import org.netbeans.api.debugger.jpda.InvalidExpressionException;
+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.models.AbstractVariable;
+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.openide.util.Exceptions;
+
+public class TruffleVariableImpl implements TruffleVariable {
+    
+    private static final String GUEST_OBJECT_TYPE = "org.netbeans.modules.debugger.jpda.backend.truffle.GuestObject";   // NOI18N
+    private static final String FIELD_NAME = "name";                            // NOI18N
+    private static final String FIELD_TYPE = "type";                            // NOI18N
+    private static final String FIELD_READABLE = "readable";                    // NOI18N
+    private static final String FIELD_WRITABLE = "writable";                    // NOI18N
+    private static final String FIELD_INTERNAL = "internal";                    // NOI18N
+    private static final String FIELD_LEAF = "leaf";                            // NOI18N
+    private static final String FIELD_DISPLAY_VALUE = "displayValue";           // NOI18N
+    private static final String METHOD_GET_CHILDREN = "getProperties";          // NOI18N
+    private static final String METHOD_GET_CHILDREN_SIG = "()[Lorg/netbeans/modules/debugger/jpda/backend/truffle/GuestObject;";  // NOI18N
+    private static final String METHOD_SET_VALUE = "setValue";                  // NOI18N
+    private static final String METHOD_SET_VALUE_SIG = "(Lcom/oracle/truffle/api/debug/DebugStackFrame;Ljava/lang/String;)Lorg/netbeans/modules/debugger/jpda/backend/truffle/GuestObject;";  // NOI18N
+    private static final String FIELD_VALUE_SOURCE = "valueSourcePosition";     // NOI18N
+    private static final String FIELD_TYPE_SOURCE = "typeSourcePosition";       // NOI18N
+    
+    private final ObjectVariable guestObject;
+    private final String name;
+    private final String type;
+    private final String displayValue;
+    private final boolean readable;
+    private final boolean writable;
+    private final boolean internal;
+    private final boolean leaf;
+    private SourcePosition valueSource;
+    private SourcePosition typeSource;
+    
+    private TruffleVariableImpl(ObjectVariable guestObject, String name,
+                                String type, String displayValue,
+                                boolean readable, boolean writable, boolean internal,
+                                boolean leaf) {
+        this.guestObject = guestObject;
+        this.name = name;
+        this.type = type;
+        this.displayValue = displayValue;
+        this.readable = readable;
+        this.writable = writable;
+        this.internal = internal;
+        this.leaf = leaf;
+    }
+    
+    public static TruffleVariableImpl get(Variable var) {
+        if (GUEST_OBJECT_TYPE.equals(var.getType())) {
+            ObjectVariable truffleObj = (ObjectVariable) var;
+            //System.err.println("TruffleVariableImpl.get("+var+") fields on "+truffleObj+", class "+truffleObj.getClassType().getName());
+            // The inner value can change in watches.
+            Field f = truffleObj.getField(FIELD_NAME);
+            if (f == null) {
+                return null;
+            }
+            String name = (String) f.createMirrorObject();
+
+            f = truffleObj.getField(FIELD_TYPE);
+            if (f == null) {
+                return null;
+            }
+            String type = (String) f.createMirrorObject();
+
+            f = truffleObj.getField(FIELD_DISPLAY_VALUE);
+            if (f == null) {
+                return null;
+            }
+            String dispVal = (String) f.createMirrorObject();
+
+            f = truffleObj.getField(FIELD_READABLE);
+            boolean readable;
+            if (f == null) {
+                readable = true;
+            } else {
+                readable = (Boolean) f.createMirrorObject();
+            }
+
+            f = truffleObj.getField(FIELD_WRITABLE);
+            boolean writable;
+            if (f == null) {
+                writable = true;
+            } else {
+                writable = (Boolean) f.createMirrorObject();
+            }
+
+            f = truffleObj.getField(FIELD_INTERNAL);
+            boolean internal;
+            if (f == null) {
+                internal = true;
+            } else {
+                internal = (Boolean) f.createMirrorObject();
+            }
+
+            boolean leaf = isLeaf(truffleObj);
+            return new TruffleVariableImpl(truffleObj, name, type, dispVal, readable, writable, internal, leaf);
+        } else {
+            return null;
+        }
+    }
+
+    static boolean isLeaf(ObjectVariable truffleObj) {
+        Field f = getFieldChecked(FIELD_LEAF, truffleObj);
+        Boolean mirrorLeaf = (Boolean) f.createMirrorObject();
+        boolean leaf;
+        if (mirrorLeaf == null) {
+            leaf = false;
+        } else {
+            leaf = mirrorLeaf;
+        }
+        return leaf;
+    }
+    
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String getType() {
+        return type;
+    }
+
+    @Override
+    public boolean isReadable() {
+        return readable;
+    }
+
+    @Override
+    public boolean isWritable() {
+        return writable;
+    }
+
+    @Override
+    public boolean isInternal() {
+        return internal;
+    }
+    
+    public String getDisplayValue() {
+        return displayValue;
+    }
+
+    @Override
+    public Object getValue() {
+        return displayValue; // TODO
+    }
+
+    @Override
+    public ObjectVariable setValue(JPDADebugger debugger, String newExpression) {
+        return setValue(debugger, guestObject, newExpression);
+    }
+
+    static ObjectVariable setValue(JPDADebugger debugger, ObjectVariable guestObject, String newExpression) {
+        CurrentPCInfo currentPCInfo = TruffleAccess.getCurrentPCInfo(debugger.getCurrentThread());
+        if (currentPCInfo != null) {
+            ObjectVariable selectedFrame = currentPCInfo.getSelectedStackFrame().getStackFrameInstance();
+            try {
+                Variable retVar = guestObject.invokeMethod(METHOD_SET_VALUE, METHOD_SET_VALUE_SIG,
+                                                             new Variable[] { selectedFrame, debugger.createMirrorVar(newExpression) });
+                if (retVar instanceof ObjectVariable) {
+                    return (ObjectVariable) retVar;
+                }
+            } catch (NoSuchMethodException | InvalidExpressionException | InvalidObjectException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public synchronized SourcePosition getValueSource() {
+        if (valueSource == null) {
+            Field f = getFieldChecked(FIELD_VALUE_SOURCE, guestObject);
+            valueSource = TruffleAccess.getSourcePosition(((AbstractVariable) f).getDebugger(), (ObjectVariable) f);
+        }
+        return valueSource;
+    }
+
+    @Override
+    public synchronized SourcePosition getTypeSource() {
+        if (typeSource == null) {
+            Field f = getFieldChecked(FIELD_TYPE_SOURCE, guestObject);
+            typeSource = TruffleAccess.getSourcePosition(((AbstractVariable) f).getDebugger(), (ObjectVariable) f);
+        }
+        return typeSource;
+    }
+
+    @Override
+    public boolean isLeaf() {
+        return leaf;
+    }
+    
+    @Override
+    public Object[] getChildren() {
+        return getChildren(guestObject);
+    }
+
+    static Object[] getChildren(ObjectVariable guestObject) {
+        try {
+            Variable children = guestObject.invokeMethod(METHOD_GET_CHILDREN, METHOD_GET_CHILDREN_SIG, new Variable[] {});
+            if (children instanceof ObjectVariable) {
+                Field[] fields = ((ObjectVariable) children).getFields(0, Integer.MAX_VALUE);
+                int n = fields.length;
+                Object[] ch = new Object[n];
+                for (int i = 0; i < n; i++) {
+                    TruffleVariableImpl tv = get(fields[i]);
+                    if (tv != null) {
+                        ch[i] = tv;
+                    } else {
+                        ch[i] = fields[i].createMirrorObject();
+                    }
+                }
+                return ch;
+            }
+        } catch (NoSuchMethodException | InvalidExpressionException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+        return new Object[] {};
+    }
+    
+    private static Field getFieldChecked(String fieldName, ObjectVariable guestObject) {
+        Field f = guestObject.getField(fieldName);
+        if (f == null) {
+            try {
+                throw new IllegalStateException("No "+fieldName+" field in "+guestObject.getToStringValue());
+            } catch (InvalidExpressionException iex) {
+                throw new IllegalStateException("No "+fieldName+" field in "+guestObject);
+            }
+        } else {
+            return f;
+        }
+    }
+}
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
new file mode 100644
index 0000000000..aa7d51813d
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/models/TruffleLocalVariablesTreeModel.java
@@ -0,0 +1,116 @@
+/*
+ * 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.vars.models;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+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.TruffleVariable;
+import org.netbeans.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
+import org.netbeans.spi.viewmodel.ModelEvent;
+import org.netbeans.spi.viewmodel.ModelListener;
+import org.netbeans.spi.viewmodel.TreeModel;
+import org.netbeans.spi.viewmodel.TreeModelFilter;
+import org.netbeans.spi.viewmodel.UnknownTypeException;
+import org.openide.util.WeakListeners;
+import org.openide.util.WeakSet;
+
+@DebuggerServiceRegistration(path="netbeans-JPDASession/"+TruffleStrataProvider.TRUFFLE_STRATUM+"/LocalsView",  types = TreeModelFilter.class)
+public class TruffleLocalVariablesTreeModel extends TruffleVariablesTreeModel {
+
+    private final WeakSet<CurrentPCInfo> cpisListening = new WeakSet<CurrentPCInfo>();
+    private final CurrentInfoPropertyChangeListener cpiChL = new CurrentInfoPropertyChangeListener();
+    
+    public TruffleLocalVariablesTreeModel(ContextProvider lookupProvider) {
+        super(lookupProvider);
+    }
+    
+    @Override
+    public Object[] getChildren(TreeModel original, Object parent, int from, int to) throws UnknownTypeException {
+        if (parent == original.getRoot()) {
+            CurrentPCInfo currentPCInfo = TruffleAccess.getCurrentPCInfo(getDebugger().getCurrentThread());
+            if (currentPCInfo != null) {
+                synchronized (cpisListening) {
+                    if (!cpisListening.contains(currentPCInfo)) {
+                        currentPCInfo.addPropertyChangeListener(
+                                WeakListeners.propertyChange(cpiChL, currentPCInfo));
+                        cpisListening.add(currentPCInfo);
+                    }
+                }
+                TruffleStackFrame selectedStackFrame = currentPCInfo.getSelectedStackFrame();
+                TruffleScope[] scopes = selectedStackFrame.getScopes();
+                if (scopes.length == 0) {
+                    return new Object[] {};
+                }
+                TruffleVariable[] innerMostVars = scopes[0].getVariables();
+                if (scopes.length == 1) {
+                    return innerMostVars;
+                }
+                Object[] varsAndScopes = new Object[innerMostVars.length + scopes.length - 1];
+                System.arraycopy(innerMostVars, 0, varsAndScopes, 0, innerMostVars.length);
+                System.arraycopy(scopes, 1, varsAndScopes, innerMostVars.length, scopes.length - 1);
+                return varsAndScopes;
+                /*
+                TruffleVariable[] vars = selectedStackFrame.getVars();
+                ObjectVariable thisObj = selectedStackFrame.getThis();
+                if (false && thisObj != null) {
+                    TruffleVariable tThis = TruffleVariableImpl.get(thisObj);
+                    if (tThis != null) {
+                        Object[] children = new Object[vars.length + 1];
+                        children[0] = tThis;
+                        System.arraycopy(vars, 0, children, 1, vars.length);
+                        return children;
+                    }
+                }
+                return vars;
+                */
+            }
+        } else if (parent instanceof TruffleScope) {
+            TruffleScope scope = (TruffleScope) parent;
+            return scope.getVariables();
+        } else if (parent instanceof TruffleVariable) {
+            return ((TruffleVariable) parent).getChildren();
+        }
+        return original.getChildren(parent, from, to);
+    }
+    
+    private void fireVarsChanged() {
+        ModelEvent evt = new ModelEvent.TreeChanged(this);
+        for (ModelListener l : listeners) {
+            l.modelChanged(evt);
+        }
+    }
+
+    private class CurrentInfoPropertyChangeListener implements PropertyChangeListener {
+
+        @Override
+        public void propertyChange(PropertyChangeEvent evt) {
+            fireVarsChanged();
+        }
+
+    }
+
+}
diff --git a/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/models/TruffleVariablesActionsProviderFilter.java b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/models/TruffleVariablesActionsProviderFilter.java
new file mode 100644
index 0000000000..0abf3932a0
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/models/TruffleVariablesActionsProviderFilter.java
@@ -0,0 +1,157 @@
+/*
+ * 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.vars.models;
+
+import java.net.URL;
+import javax.swing.Action;
+import javax.swing.SwingUtilities;
+
+import org.netbeans.modules.debugger.jpda.EditorContextBridge;
+import org.netbeans.modules.debugger.jpda.truffle.access.TruffleStrataProvider;
+import org.netbeans.modules.debugger.jpda.truffle.source.SourcePosition;
+import org.netbeans.modules.debugger.jpda.truffle.vars.TruffleVariable;
+import org.netbeans.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
+import org.netbeans.spi.debugger.DebuggerServiceRegistrations;
+import org.netbeans.spi.viewmodel.Models;
+import org.netbeans.spi.viewmodel.NodeActionsProvider;
+import org.netbeans.spi.viewmodel.NodeActionsProviderFilter;
+import org.netbeans.spi.viewmodel.UnknownTypeException;
+import org.openide.DialogDisplayer;
+import org.openide.NotifyDescriptor;
+import org.openide.util.NbBundle;
+
+@DebuggerServiceRegistrations({
+    @DebuggerServiceRegistration(path="netbeans-JPDASession/"+TruffleStrataProvider.TRUFFLE_STRATUM+"/LocalsView",  types=NodeActionsProviderFilter.class),
+    @DebuggerServiceRegistration(path="netbeans-JPDASession/"+TruffleStrataProvider.TRUFFLE_STRATUM+"/ResultsView", types=NodeActionsProviderFilter.class),
+    @DebuggerServiceRegistration(path="netbeans-JPDASession/"+TruffleStrataProvider.TRUFFLE_STRATUM+"/ToolTipView", types=NodeActionsProviderFilter.class),
+    @DebuggerServiceRegistration(path="netbeans-JPDASession/"+TruffleStrataProvider.TRUFFLE_STRATUM+"/WatchesView", types=NodeActionsProviderFilter.class),
+})
+public class TruffleVariablesActionsProviderFilter implements NodeActionsProviderFilter {
+    
+    public TruffleVariablesActionsProviderFilter(ContextProvider contextProvider) {
+        System.err.println("new TruffleVariablesActionsProviderFilter()");
+    }
+
+    @NbBundle.Messages("CTL_GoToSource=Go to source")
+    private final Action GO_TO_VALUE_SOURCE_ACTION = Models.createAction (
+        Bundle.CTL_GoToSource(),
+        new Models.ActionPerformer () {
+            @Override
+            public boolean isEnabled (Object node) {
+                return true;
+            }
+            @Override
+            public void perform (final Object[] nodes) {
+                TruffleVariable var = (TruffleVariable) nodes[0];
+                showSource(var.getValueSource());
+            }
+        },
+        Models.MULTISELECTION_TYPE_EXACTLY_ONE
+    );
+        
+    @NbBundle.Messages("CTL_GoToTypeSource=Go to type")
+    private final Action GO_TO_TYPE_SOURCE_ACTION = Models.createAction (
+        Bundle.CTL_GoToTypeSource(),
+        new Models.ActionPerformer () {
+            @Override
+            public boolean isEnabled (Object node) {
+                return true;
+            }
+            @Override
+            public void perform (final Object[] nodes) {
+                TruffleVariable var = (TruffleVariable) nodes[0];
+                showSource(var.getTypeSource());
+            }
+        },
+        Models.MULTISELECTION_TYPE_EXACTLY_ONE
+    );
+
+    @Override
+    public void performDefaultAction(NodeActionsProvider original, Object node) throws UnknownTypeException {
+        boolean shown = false;
+        if (node instanceof TruffleVariable) {
+            TruffleVariable var = (TruffleVariable) node;
+            SourcePosition source = var.getValueSource();
+            if (source == null) {
+                source = var.getTypeSource();
+            }
+            if (source != null) {
+                showSource(source);
+                shown = true;
+            }
+        }
+        if (!shown) {
+            original.performDefaultAction(node);
+        }
+    }
+
+    @Override
+    public Action[] getActions(NodeActionsProvider original, Object node) throws UnknownTypeException {
+        UnknownTypeException originalUTEx = null;
+        Action [] actions;
+        try {
+            actions = original.getActions (node);
+        } catch (UnknownTypeException utex) {
+            originalUTEx = utex;
+            actions = new Action[0];
+        }
+        if (node instanceof TruffleVariable) {
+            TruffleVariable var = (TruffleVariable) node;
+            SourcePosition valueSource = var.getValueSource();
+            SourcePosition typeSource = var.getTypeSource();
+            if (valueSource != null || typeSource != null) {
+                int l = actions.length;
+                if (valueSource != null) {
+                    l++;
+                }
+                if (typeSource != null) {
+                    l++;
+                }
+                Action[] newActions = new Action[l];
+                System.arraycopy(actions, 0, newActions, 0, actions.length);
+                l = actions.length;
+                if (valueSource != null) {
+                    newActions[l++] = GO_TO_VALUE_SOURCE_ACTION;
+                }
+                if (typeSource != null) {
+                    newActions[l++] = GO_TO_TYPE_SOURCE_ACTION;
+                }
+                actions = newActions;
+            }
+        } else if (originalUTEx != null) {
+            throw originalUTEx;
+        }
+        return actions;
+    }
+
+    @NbBundle.Messages({"# {0} - The file path", "MSG_NoSourceFile=Cannot find source file {0}."})
+    private void showSource(SourcePosition source) {
+        URL url = source.getSource().getUrl();
+        int lineNumber = source.getLine();
+        SwingUtilities.invokeLater (() -> {
+            boolean success = EditorContextBridge.getContext().showSource(url.toExternalForm(), lineNumber, null);
+            if (!success) {
+                NotifyDescriptor d = new NotifyDescriptor.Message(Bundle.MSG_NoSourceFile(url.toExternalForm()), NotifyDescriptor.WARNING_MESSAGE);
+                DialogDisplayer.getDefault().notifyLater(d);
+            }
+        });
+    }
+}
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
new file mode 100644
index 0000000000..a0d7266d1a
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/models/TruffleVariablesNodeModel.java
@@ -0,0 +1,202 @@
+/*
+ * 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.vars.models;
+
+import java.awt.datatransfer.Transferable;
+import java.io.IOException;
+import java.util.List;
+
+import org.netbeans.api.debugger.jpda.Field;
+import org.netbeans.api.debugger.jpda.InvalidExpressionException;
+import org.netbeans.api.debugger.jpda.JPDAClassType;
+import org.netbeans.api.debugger.jpda.ObjectVariable;
+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.TruffleVariable;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
+import org.netbeans.spi.debugger.DebuggerServiceRegistrations;
+import org.netbeans.spi.viewmodel.ExtendedNodeModel;
+import org.netbeans.spi.viewmodel.ExtendedNodeModelFilter;
+import org.netbeans.spi.viewmodel.ModelListener;
+import org.netbeans.spi.viewmodel.NodeModel;
+import org.netbeans.spi.viewmodel.UnknownTypeException;
+import org.openide.util.datatransfer.PasteType;
+
+@DebuggerServiceRegistrations({
+    @DebuggerServiceRegistration(path="netbeans-JPDASession/"+TruffleStrataProvider.TRUFFLE_STRATUM+"/LocalsView",  types=ExtendedNodeModelFilter.class),
+    @DebuggerServiceRegistration(path="netbeans-JPDASession/"+TruffleStrataProvider.TRUFFLE_STRATUM+"/ResultsView", types=ExtendedNodeModelFilter.class),
+    @DebuggerServiceRegistration(path="netbeans-JPDASession/"+TruffleStrataProvider.TRUFFLE_STRATUM+"/ToolTipView", types=ExtendedNodeModelFilter.class),
+    @DebuggerServiceRegistration(path="netbeans-JPDASession/"+TruffleStrataProvider.TRUFFLE_STRATUM+"/WatchesView", types=ExtendedNodeModelFilter.class),
+})
+public class TruffleVariablesNodeModel implements ExtendedNodeModelFilter {
+
+    @Override
+    public boolean canRename(ExtendedNodeModel original, Object node) throws UnknownTypeException {
+        return original.canRename(node);
+    }
+
+    @Override
+    public boolean canCopy(ExtendedNodeModel original, Object node) throws UnknownTypeException {
+        return original.canCopy(node);
+    }
+
+    @Override
+    public boolean canCut(ExtendedNodeModel original, Object node) throws UnknownTypeException {
+        return original.canCut(node);
+    }
+
+    @Override
+    public Transferable clipboardCopy(ExtendedNodeModel original, Object node) throws IOException, UnknownTypeException {
+        return original.clipboardCopy(node);
+    }
+
+    @Override
+    public Transferable clipboardCut(ExtendedNodeModel original, Object node) throws IOException, UnknownTypeException {
+        return original.clipboardCut(node);
+    }
+
+    @Override
+    public PasteType[] getPasteTypes(ExtendedNodeModel original, Object node, Transferable t) throws UnknownTypeException {
+        return original.getPasteTypes(node, t);
+    }
+
+    @Override
+    public void setName(ExtendedNodeModel original, Object node, String name) throws UnknownTypeException {
+        original.setName(node, name);
+    }
+
+    @Override
+    public String getIconBaseWithExtension(ExtendedNodeModel original, Object node) throws UnknownTypeException {
+        if (node instanceof TruffleVariable) {
+            String name = ((TruffleVariable) node).getName();
+            if ("this".equals(name)) {
+                return original.getIconBaseWithExtension(EmptyThis.INSTANCE);
+            } else {
+                return original.getIconBaseWithExtension(EmptyVar.INSTANCE);
+            }
+        }
+        return original.getIconBaseWithExtension(node);
+    }
+
+    @Override
+    public String getDisplayName(NodeModel original, Object node) throws UnknownTypeException {
+        if (node instanceof TruffleScope) {
+            return ((TruffleScope) node).getName();
+        }
+        if (node instanceof TruffleVariable) {
+            return ((TruffleVariable) node).getName();
+        } else {
+            return original.getDisplayName(node);
+        }
+    }
+
+    @Override
+    public String getIconBase(NodeModel original, Object node) throws UnknownTypeException {
+        throw new UnsupportedOperationException("Not supported.");
+    }
+
+    @Override
+    public String getShortDescription(NodeModel original, Object node) throws UnknownTypeException {
+        if (node instanceof TruffleVariable) {
+            TruffleVariable var = (TruffleVariable) node;
+            return var.getName() + " = " + var.getValue();
+        }
+        return original.getShortDescription(node);
+    }
+
+    @Override
+    public void addModelListener(ModelListener l) {
+    }
+
+    @Override
+    public void removeModelListener(ModelListener l) {
+    }
+    
+    private static final class EmptyThis implements This {
+        
+        static This INSTANCE = new EmptyThis();
+
+        @Override
+        public String getToStringValue() throws InvalidExpressionException { return "empty"; }
+
+        @Override
+        public Variable invokeMethod(String methodName, String signature, Variable[] arguments) throws NoSuchMethodException, InvalidExpressionException {
+            throw new UnsupportedOperationException("Not supported.");
+        }
+
+        @Override
+        public int getFieldsCount() { return  0; }
+
+        @Override
+        public Field getField(String name) { return null; }
+
+        @Override
+        public Field[] getFields(int from, int to) { return null; }
+
+        @Override
+        public Field[] getAllStaticFields(int from, int to) { return null; }
+
+        @Override
+        public Field[] getInheritedFields(int from, int to) { return null; }
+
+        @Override
+        public List<ObjectVariable> getReferringObjects(long maxReferrers) throws UnsupportedOperationException {
+            return null;
+        }
+
+        @Override
+        public Super getSuper() { return null; }
+
+        @Override
+        public JPDAClassType getClassType() { return null; }
+
+        @Override
+        public long getUniqueID() { return 0l; }
+
+        @Override
+        public String getType() { return "empty"; }
+
+        @Override
+        public String getValue() { return ""; }
+
+        @Override
+        public Object createMirrorObject() { return null; }
+        
+    }
+    
+    private static final class EmptyVar implements Variable {
+        
+        static Variable INSTANCE = new EmptyVar();
+
+        @Override
+        public String getType() { return "empty"; }
+
+        @Override
+        public String getValue() { return ""; }
+
+        @Override
+        public Object createMirrorObject() { return null; }
+        
+    }
+    
+}
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
new file mode 100644
index 0000000000..69b2bad4bb
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/models/TruffleVariablesTableModel.java
@@ -0,0 +1,151 @@
+/*
+ * 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.vars.models;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.JPDAWatch;
+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.TruffleVariable;
+import org.netbeans.modules.debugger.jpda.truffle.vars.TruffleVariableImpl;
+import org.netbeans.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
+import org.netbeans.spi.debugger.DebuggerServiceRegistrations;
+import static org.netbeans.spi.debugger.ui.Constants.LOCALS_TO_STRING_COLUMN_ID;
+import static org.netbeans.spi.debugger.ui.Constants.LOCALS_TYPE_COLUMN_ID;
+import static org.netbeans.spi.debugger.ui.Constants.LOCALS_VALUE_COLUMN_ID;
+import static org.netbeans.spi.debugger.ui.Constants.WATCH_TO_STRING_COLUMN_ID;
+import static org.netbeans.spi.debugger.ui.Constants.WATCH_TYPE_COLUMN_ID;
+import static org.netbeans.spi.debugger.ui.Constants.WATCH_VALUE_COLUMN_ID;
+import org.netbeans.spi.viewmodel.ModelEvent;
+import org.netbeans.spi.viewmodel.ModelListener;
+import org.netbeans.spi.viewmodel.TableHTMLModel;
+import org.netbeans.spi.viewmodel.TableHTMLModelFilter;
+import org.netbeans.spi.viewmodel.TableModel;
+import org.netbeans.spi.viewmodel.TableModelFilter;
+import org.netbeans.spi.viewmodel.UnknownTypeException;
+
+@DebuggerServiceRegistrations({
+    @DebuggerServiceRegistration(path="netbeans-JPDASession/"+TruffleStrataProvider.TRUFFLE_STRATUM+"/LocalsView",  types = TableModelFilter.class),
+    @DebuggerServiceRegistration(path="netbeans-JPDASession/"+TruffleStrataProvider.TRUFFLE_STRATUM+"/ResultsView", types = TableModelFilter.class),
+    @DebuggerServiceRegistration(path="netbeans-JPDASession/"+TruffleStrataProvider.TRUFFLE_STRATUM+"/ToolTipView", types = TableModelFilter.class),
+    @DebuggerServiceRegistration(path="netbeans-JPDASession/"+TruffleStrataProvider.TRUFFLE_STRATUM+"/WatchesView", types = TableModelFilter.class)
+})
+public class TruffleVariablesTableModel implements TableModelFilter, TableHTMLModelFilter {
+    
+    private final JPDADebugger debugger;
+    private final List<ModelListener> listeners = new CopyOnWriteArrayList<ModelListener>();
+    
+    public TruffleVariablesTableModel(ContextProvider contextProvider) {
+        debugger = contextProvider.lookupFirst(null, JPDADebugger.class);
+    }
+
+    @Override
+    public Object getValueAt(TableModel original, Object node, String columnID) throws UnknownTypeException {
+        if (node instanceof TruffleScope) {
+            return "";
+        }
+        TruffleVariable tv = null;
+        if (node instanceof JPDAWatch) {// && !isEnabled((JPDAWatch) node)) {
+            Object orig = original.getValueAt(node, columnID); // Call in any case because of error displaying
+            if (node instanceof Variable) {
+                tv = TruffleVariableImpl.get((Variable) node);
+            }
+            if (tv == null) {
+                return orig;
+            }
+        } else if (node instanceof TruffleVariable) {
+            tv = (TruffleVariable) node;
+        }
+        if (tv != null) {
+            switch (columnID) {
+                case LOCALS_TYPE_COLUMN_ID:
+                case WATCH_TYPE_COLUMN_ID:
+                    return tv.getType();
+                case LOCALS_VALUE_COLUMN_ID:
+                case WATCH_VALUE_COLUMN_ID:
+                    return tv.getValue();
+                case LOCALS_TO_STRING_COLUMN_ID:
+                case WATCH_TO_STRING_COLUMN_ID:
+                    Object var = tv.getValue();
+                    return String.valueOf(var);
+            }
+        }
+        return original.getValueAt(node, columnID);
+    }
+
+    @Override
+    public boolean isReadOnly(TableModel original, Object node, String columnID) throws UnknownTypeException {
+        if (node instanceof TruffleScope) {
+            return true;
+        }
+        if (node instanceof TruffleVariable) {
+            if (LOCALS_VALUE_COLUMN_ID.equals(columnID) || WATCH_VALUE_COLUMN_ID.equals(columnID)) {
+                return !((TruffleVariable) node).isWritable();
+            } else {
+                return true;
+            }
+        }
+        return original.isReadOnly(node, columnID);
+    }
+
+    @Override
+    public void setValueAt(TableModel original, Object node, String columnID, Object value) throws UnknownTypeException {
+        if (node instanceof TruffleVariable) {
+            boolean success = ((TruffleVariable) node).setValue(debugger, value.toString()) != null;
+            if (success) {
+                ModelEvent evt = new ModelEvent.NodeChanged(this, node);
+                for (ModelListener l : listeners) {
+                    l.modelChanged(evt);
+                }
+            }
+        } else {
+            original.setValueAt(node, columnID, value);
+        }
+    }
+
+    @Override
+    public boolean hasHTMLValueAt(TableHTMLModel original, Object node, String columnID) throws UnknownTypeException {
+        if (node instanceof TruffleVariable) {
+            return false;
+        }
+        return original.hasHTMLValueAt(node, columnID);
+    }
+
+    @Override
+    public String getHTMLValueAt(TableHTMLModel original, Object node, String columnID) throws UnknownTypeException {
+        return original.getHTMLValueAt(node, columnID);
+    }
+
+    @Override
+    public void addModelListener(ModelListener l) {
+        listeners.add(l);
+    }
+
+    @Override
+    public void removeModelListener(ModelListener l) {
+        listeners.remove(l);
+    }
+
+}
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
new file mode 100644
index 0000000000..cf9f1190d3
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/models/TruffleVariablesTreeModel.java
@@ -0,0 +1,107 @@
+/*
+ * 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.vars.models;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.JPDAWatch;
+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.TruffleVariable;
+import org.netbeans.modules.debugger.jpda.truffle.vars.TruffleVariableImpl;
+import org.netbeans.spi.debugger.ContextProvider;
+import org.netbeans.spi.debugger.DebuggerServiceRegistration;
+import org.netbeans.spi.debugger.DebuggerServiceRegistrations;
+import org.netbeans.spi.viewmodel.ModelListener;
+import org.netbeans.spi.viewmodel.TreeModel;
+import org.netbeans.spi.viewmodel.TreeModelFilter;
+import org.netbeans.spi.viewmodel.UnknownTypeException;
+
+@DebuggerServiceRegistrations({
+    @DebuggerServiceRegistration(path="netbeans-JPDASession/"+TruffleStrataProvider.TRUFFLE_STRATUM+"/ResultsView", types = TreeModelFilter.class),
+    @DebuggerServiceRegistration(path="netbeans-JPDASession/"+TruffleStrataProvider.TRUFFLE_STRATUM+"/ToolTipView", types = TreeModelFilter.class),
+    @DebuggerServiceRegistration(path="netbeans-JPDASession/"+TruffleStrataProvider.TRUFFLE_STRATUM+"/WatchesView", types = TreeModelFilter.class),
+})
+public class TruffleVariablesTreeModel implements TreeModelFilter {
+    
+    private final JPDADebugger debugger;
+    protected final List<ModelListener> listeners = new CopyOnWriteArrayList<ModelListener>();
+    
+    public TruffleVariablesTreeModel(ContextProvider lookupProvider) {
+        debugger = lookupProvider.lookupFirst(null, JPDADebugger.class);
+    }
+    
+    protected JPDADebugger getDebugger() {
+        return debugger;
+    }
+
+    @Override
+    public Object getRoot(TreeModel original) {
+        return original.getRoot();
+    }
+
+    @Override
+    public Object[] getChildren(TreeModel original, Object parent, int from, int to) throws UnknownTypeException {
+        if (parent instanceof JPDAWatch) {
+            TruffleVariable tv = TruffleVariableImpl.get((Variable) parent);
+            if (tv != null) {
+                parent = tv;
+            }
+        }
+        if (parent instanceof TruffleScope) {
+            TruffleScope scope = (TruffleScope) parent;
+            return scope.getVariables();
+        }
+        if (parent instanceof TruffleVariable) {
+            return ((TruffleVariable) parent).getChildren();
+        }
+        return original.getChildren(parent, from, to);
+    }
+
+    @Override
+    public int getChildrenCount(TreeModel original, Object node) throws UnknownTypeException {
+        return Integer.MAX_VALUE;
+    }
+
+    @Override
+    public boolean isLeaf(TreeModel original, Object node) throws UnknownTypeException {
+        if (node instanceof TruffleScope) {
+            return false;
+        }
+        if (node instanceof TruffleVariable) {
+            return ((TruffleVariable) node).isLeaf();
+        } else {
+            return original.isLeaf(node);
+        }
+    }
+
+    @Override
+    public void addModelListener(ModelListener l) {
+        listeners.add(l);
+    }
+
+    @Override
+    public void removeModelListener(ModelListener l) {
+        listeners.remove(l);
+    }
+}
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
new file mode 100644
index 0000000000..8a89c1c633
--- /dev/null
+++ b/java/debugger.jpda.truffle/src/org/netbeans/modules/debugger/jpda/truffle/vars/tooltip/ToolTipAnnotation.java
@@ -0,0 +1,268 @@
+/*
+ * 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.vars.tooltip;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import javax.swing.JEditorPane;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Element;
+import javax.swing.text.StyledDocument;
+
+import org.netbeans.api.debugger.DebuggerEngine;
+import org.netbeans.api.debugger.DebuggerManager;
+import org.netbeans.api.debugger.Session;
+import org.netbeans.api.debugger.jpda.CallStackFrame;
+import org.netbeans.api.debugger.jpda.InvalidExpressionException;
+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.access.TruffleEval;
+import org.netbeans.modules.debugger.jpda.truffle.access.TruffleStrataProvider;
+import org.netbeans.modules.debugger.jpda.truffle.vars.TruffleVariableImpl;
+import org.netbeans.spi.debugger.ui.EditorContextDispatcher;
+import org.openide.cookies.EditorCookie;
+import org.openide.loaders.DataObject;
+import org.openide.text.Annotation;
+import org.openide.text.DataEditorSupport;
+import org.openide.text.Line;
+import org.openide.text.NbDocument;
+import org.openide.util.RequestProcessor;
+
+public class ToolTipAnnotation extends Annotation implements Runnable {
+    
+    private static final Set<String> JS_KEYWORDS = new HashSet<>(Arrays.asList(new String[] {
+        "break",    "case",     "catch",    "class",    "continue",
+        "debugger", "default",  "delete",   "do",       "else",
+        "enum",     "export",   "extends",  "finally",  "for",
+        "function", "if",       "implements","import",  "in",
+        "instanceof","interface","let",     "new",      "return",
+        "package",  "private",  "protected","public",   "static",
+        "super",    "switch",   /*"this",*/ "throw",    "try",
+        "typeof",   "var",      "void",     "while",    "with",
+        "yield",
+    }));
+    private static final int MAX_TOOLTIP_TEXT = 100000;
+
+    private Line.Part lp;
+    private EditorCookie ec;
+    private final RequestProcessor RP = new RequestProcessor(ToolTipAnnotation.class);
+
+    @Override
+    public String getShortDescription () {
+        Session session = DebuggerManager.getDebuggerManager ().getCurrentSession();
+        if (session == null) {
+            return null;
+        }
+        DebuggerEngine engine = session.getCurrentEngine();
+        if (engine != session.getEngineForLanguage(TruffleStrataProvider.TRUFFLE_STRATUM)) {
+            return null;
+        }
+        JPDADebugger d = engine.lookupFirst(null, JPDADebugger.class);
+        if (d == null) {
+            return null;
+        }
+
+        Line.Part lp = (Line.Part) getAttachedAnnotatable();
+        if (lp == null) {
+            return null;
+        }
+        Line line = lp.getLine ();
+        DataObject dob = DataEditorSupport.findDataObject (line);
+        if (dob == null) {
+            return null;
+        }
+        EditorCookie ec = dob.getLookup().lookup(EditorCookie.class);
+        if (ec == null) {
+            return null;
+            // Only for editable dataobjects
+        }
+
+        this.lp = lp;
+        this.ec = ec;
+        RP.post(this);
+        return null;
+    }
+
+    @Override
+    public String getAnnotationType() {
+        return null;
+    }
+
+    @Override
+    public void run() {
+        ObjectVariable tooltipVariable = null;
+        if (lp == null || ec == null) {
+            return ;
+        }
+        StyledDocument doc;
+        try {
+            doc = ec.openDocument();
+        } catch (IOException ex) {
+            return ;
+        }
+        final JEditorPane ep = EditorContextDispatcher.getDefault().getMostRecentEditor ();
+        if (ep == null || ep.getDocument() != doc) {
+            return ;
+        }
+        Session session = DebuggerManager.getDebuggerManager ().getCurrentSession();
+        if (session == null) {
+            return ;
+        }
+        DebuggerEngine engine = session.getCurrentEngine();
+        if (engine != session.getEngineForLanguage(TruffleStrataProvider.TRUFFLE_STRATUM)) {
+            return ;
+        }
+        JPDADebugger d = engine.lookupFirst(null, JPDADebugger.class);
+        if (d == null) {
+            return ;
+        }
+        CallStackFrame frame = d.getCurrentCallStackFrame();
+        if (frame == null) {
+            return ;
+        }
+
+        int offset;
+        boolean[] isFunctionPtr = new boolean[] { false };
+        final String expression = getIdentifier (
+            d,
+            doc,
+            ep,
+            offset = NbDocument.findLineOffset (
+                doc,
+                lp.getLine ().getLineNumber ()
+            ) + lp.getColumn (),
+            isFunctionPtr
+        );
+        if (expression == null) {
+            return;
+        }
+
+        String toolTipText;
+        if (isFunctionPtr[0]) {
+            //return ; // We do not call functions
+        }
+        try {
+            Variable result = TruffleEval.evaluate(d, expression);
+            if (result == null) {
+                return ; // Something went wrong...
+            }
+            TruffleVariableImpl tv = TruffleVariableImpl.get(result);
+            String displayVal;
+            if (tv != null) {
+                displayVal = tv.getDisplayValue();
+            } else {
+                displayVal = result.getValue();
+            }
+            toolTipText = expression + " = " + displayVal;
+        } catch (InvalidExpressionException ex) {
+            toolTipText = expression + " = >" + ex.getMessage () + "<";
+        }
+        toolTipText = truncateLongText(toolTipText);
+        
+        firePropertyChange (PROP_SHORT_DESCRIPTION, null, toolTipText);
+    }
+    
+    private static String getIdentifier (
+        JPDADebugger debugger,
+        StyledDocument doc,
+        JEditorPane ep,
+        int offset,
+        boolean[] isFunctionPtr
+    ) {
+        // do always evaluation if the tooltip is invoked on a text selection
+        String t = null;
+        if ( (ep.getSelectionStart () <= offset) &&
+             (offset <= ep.getSelectionEnd ())
+        ) {
+            t = ep.getSelectedText ();
+        }
+        if (t != null) {
+            return t;
+        }
+        int line = NbDocument.findLineNumber (
+            doc,
+            offset
+        );
+        int col = NbDocument.findLineColumn (
+            doc,
+            offset
+        );
+        try {
+            Element lineElem =
+                NbDocument.findLineRootElement (doc).
+                getElement (line);
+
+            if (lineElem == null) {
+                return null;
+            }
+            int lineStartOffset = lineElem.getStartOffset ();
+            int lineLen = lineElem.getEndOffset() - lineStartOffset;
+            t = doc.getText (lineStartOffset, lineLen);
+            int identStart = col;
+            while (identStart > 0 &&
+                (Character.isJavaIdentifierPart (
+                    t.charAt (identStart - 1)
+                ) ||
+                (t.charAt (identStart - 1) == '.'))) {
+                identStart--;
+            }
+            int identEnd = col;
+            while (identEnd < lineLen &&
+                   Character.isJavaIdentifierPart(t.charAt(identEnd))
+            ) {
+                identEnd++;
+            }
+
+            if (identStart == identEnd) {
+                return null;
+            }
+
+            String ident = t.substring (identStart, identEnd);
+            //if (JS_KEYWORDS.contains(ident)) {
+                // JS keyword => Do not show anything
+            //    return null;
+            //}
+
+            while (identEnd < lineLen &&
+                   Character.isWhitespace(t.charAt(identEnd))
+            ) {
+                identEnd++;
+            }
+            if (identEnd < lineLen && t.charAt(identEnd) == '(') {
+                // We're at a function call
+                isFunctionPtr[0] = true;
+            }
+            return ident;
+        } catch (BadLocationException e) {
+            return null;
+        }
+    }
+
+    private static String truncateLongText(String text) {
+        if (text.length() > MAX_TOOLTIP_TEXT) {
+            text = text.substring(0, MAX_TOOLTIP_TEXT) + "...";
+        }
+        return text;
+    }
+
+}
diff --git a/java/debugger.jpda.truffle/test/unit/src/org/netbeans/modules/debugger/jpda/truffle/DebugSLTest.java b/java/debugger.jpda.truffle/test/unit/src/org/netbeans/modules/debugger/jpda/truffle/DebugSLTest.java
new file mode 100644
index 0000000000..dc3ab848b1
--- /dev/null
+++ b/java/debugger.jpda.truffle/test/unit/src/org/netbeans/modules/debugger/jpda/truffle/DebugSLTest.java
@@ -0,0 +1,193 @@
+/*
+ * 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.oracle.truffle.api.TruffleLanguage;
+import com.oracle.truffle.sl.SLLanguage;
+import java.io.File;
+import java.lang.reflect.Field;
+import java.net.URISyntaxException;
+import java.net.URL;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import org.graalvm.polyglot.Engine;
+import org.netbeans.api.debugger.DebuggerManager;
+import org.netbeans.api.debugger.jpda.CallStackFrame;
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.JPDASupport;
+import org.netbeans.api.debugger.jpda.LineBreakpoint;
+import org.netbeans.junit.NbTestCase;
+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.breakpoints.TruffleLineBreakpoint;
+import org.netbeans.modules.debugger.jpda.truffle.frames.TruffleStackFrame;
+import org.netbeans.modules.debugger.jpda.truffle.source.SourcePosition;
+import org.netbeans.modules.javascript2.debug.EditorLineHandlerFactory;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.URLMapper;
+
+public class DebugSLTest extends NbTestCase {
+    private DebuggerManager dm = DebuggerManager.getDebuggerManager();
+    private final String sourceRoot = System.getProperty("test.dir.src");
+    private JPDASupport support;
+
+    public DebugSLTest(String name) {
+        super(name);
+    }
+
+    public static Test suite() throws URISyntaxException {
+        final File sdkAPI = new File(Engine.class.getProtectionDomain().getCodeSource().getLocation().toURI());
+        final File truffleAPI = new File(TruffleLanguage.class.getProtectionDomain().getCodeSource().getLocation().toURI());
+        final File sl = new File(SLLanguage.class.getProtectionDomain().getCodeSource().getLocation().toURI());
+        final File antlr4 = new File(org.antlr.v4.runtime.Parser.class.getProtectionDomain().getCodeSource().getLocation().toURI());
+        final File junit = new File(TestCase.class.getProtectionDomain().getCodeSource().getLocation().toURI());
+        assertTrue("SDK API exists: " + sdkAPI, sdkAPI.exists());
+        assertTrue("truffle-api JAR exists: " + truffleAPI, truffleAPI.exists());
+        assertTrue("sl JAR exists: " + sl, sl.exists());
+        assertTrue("antlr4 JAR exists: " + antlr4, antlr4.exists());
+        assertTrue("junit JAR exists: " + junit, junit.exists());
+
+        System.setProperty("graal-sdk.jar", sdkAPI.getAbsolutePath());
+        System.setProperty("truffle.jar", truffleAPI.getAbsolutePath());
+        System.setProperty("sl.jar", sl.getAbsolutePath());
+        System.setProperty("antlr4.jar", antlr4.getAbsolutePath());
+        System.setProperty("junit.jar", junit.getAbsolutePath());
+
+        return JPDASupport.createTestSuite(DebugSLTest.class);
+    }
+
+    public void testStepIntoMainSL() throws Exception {
+        doStepIntoSL(0, "main", 2);
+    }
+    
+    public void testStepIntoInvokeAs() throws Exception {
+        doStepIntoSL(1, "init", 7);
+    }
+    
+    public void testStepIntoDynamicInterface() throws Exception {
+        doStepIntoSL(2, "main", 2);
+    }
+    
+    private void doStepIntoSL(int bpNum, String methodName, int lineNo) throws Exception {
+        try {
+            JPDASupport.removeAllBreakpoints();
+            org.netbeans.api.debugger.jpda.Utils.BreakPositions bp = org.netbeans.api.debugger.jpda.Utils.getBreakPositions(
+                sourceRoot
+                + "org/netbeans/modules/debugger/jpda/truffle/testapps/SLApp.java");
+            LineBreakpoint lb = bp.getLineBreakpoints().get(bpNum);
+            dm.addBreakpoint(lb);
+            support = JPDASupport.attach("org.netbeans.modules.debugger.jpda.truffle.testapps.SLApp",
+                new String[0],
+                new File[] {
+                    new File(System.getProperty("graal-sdk.jar")),
+                    new File(System.getProperty("truffle.jar")),
+                    new File(System.getProperty("antlr4.jar")),
+                    new File(System.getProperty("sl.jar")),
+                    new File(System.getProperty("junit.jar")),
+                }
+            );
+            support.waitState(JPDADebugger.STATE_STOPPED);
+            support.stepInto();
+            final JPDADebugger debugger = support.getDebugger();
+            CallStackFrame frame = debugger.getCurrentCallStackFrame();
+            assertNotNull(frame);
+            // Check that frame is in the Truffle access method
+            String haltedClass = TruffleAccess.BASIC_CLASS_NAME;
+            Field haltedMethodField = TruffleAccess.class.getDeclaredField("METHOD_EXEC_HALTED");
+            haltedMethodField.setAccessible(true);
+            String haltedMethod = (String) haltedMethodField.get(null);
+            /* Debug where it's stopped at:
+            System.err.println("Stopped in "+frame.getClassName()+"."+frame.getMethodName()+"()");
+            CallStackFrame[] callStack = frame.getThread().getCallStack();
+            for (CallStackFrame sf : callStack) {
+                System.err.println("  at "+sf.getClassName()+"."+sf.getMethodName()+"():"+sf.getLineNumber(null)+"  stratum: "+sf.getDefaultStratum());
+            }*/
+            assertEquals("Stopped in Truffle halted class", haltedClass, frame.getClassName());
+            assertEquals("Stopped in Truffle halted method", haltedMethod, frame.getMethodName());
+            assertEquals("Unexpected stratum", TruffleStrataProvider.TRUFFLE_STRATUM, frame.getDefaultStratum());
+            
+            CurrentPCInfo currentPCInfo = TruffleAccess.getCurrentPCInfo(frame.getThread());
+            assertNotNull("Missing CurrentPCInfo", currentPCInfo);
+            TruffleStackFrame topFrame = currentPCInfo.getTopFrame();
+            assertNotNull("No top frame", topFrame);
+            SourcePosition sourcePosition = topFrame.getSourcePosition();
+            assertEquals("Bad source", "Meaning of world.sl", sourcePosition.getSource().getName());
+            assertEquals("Bad line", lineNo, sourcePosition.getLine());
+            assertEquals("Bad method name", methodName, topFrame.getMethodName());
+            
+            support.doContinue();
+            support.waitState(JPDADebugger.STATE_DISCONNECTED);
+        } finally {
+            if (support != null) {
+                support.doFinish();
+            }
+        }
+    }
+
+    public void testBreakpointsInSL() throws Exception {
+        try {
+            JPDASupport.removeAllBreakpoints();
+            org.netbeans.api.debugger.jpda.Utils.BreakPositions bp = org.netbeans.api.debugger.jpda.Utils.getBreakPositions(
+                sourceRoot
+                + "org/netbeans/modules/debugger/jpda/truffle/testapps/TestApp.sl");
+            LineBreakpoint lb = bp.getLineBreakpoints().get(0);
+            // An ugly way to transform the Java breakpoint into Truffle breakpoint, used in SL.
+            FileObject fo = URLMapper.findFileObject(new URL(lb.getURL()));
+            dm.addBreakpoint(new TruffleLineBreakpoint(EditorLineHandlerFactory.getHandler(fo, lb.getLineNumber())));
+            support = JPDASupport.attach("org.netbeans.modules.debugger.jpda.truffle.testapps.SLAppFromFile",
+                new String[] {
+                    sourceRoot + "org/netbeans/modules/debugger/jpda/truffle/testapps/TestApp.sl"
+                },
+                new File[] {
+                    new File(System.getProperty("graal-sdk.jar")),
+                    new File(System.getProperty("truffle.jar")),
+                    new File(System.getProperty("antlr4.jar")),
+                    new File(System.getProperty("sl.jar")),
+                    new File(System.getProperty("junit.jar")),
+                }
+            );
+            support.waitState(JPDADebugger.STATE_STOPPED);
+            
+            final JPDADebugger debugger = support.getDebugger();
+            CallStackFrame frame = debugger.getCurrentCallStackFrame();
+            assertNotNull(frame);
+            // Check that frame is in the Truffle guest language
+            assertEquals("Unexpected stratum", TruffleStrataProvider.TRUFFLE_STRATUM, frame.getDefaultStratum());
+            
+            CurrentPCInfo currentPCInfo = TruffleAccess.getCurrentPCInfo(frame.getThread());
+            assertNotNull("Missing CurrentPCInfo", currentPCInfo);
+            TruffleStackFrame topFrame = currentPCInfo.getTopFrame();
+            assertNotNull("No top frame", topFrame);
+            SourcePosition sourcePosition = topFrame.getSourcePosition();
+            assertEquals("Bad source", "TestApp.sl", sourcePosition.getSource().getName());
+            assertEquals("Bad line", lb.getLineNumber(), sourcePosition.getLine());
+            assertEquals("Bad method name", "main", topFrame.getMethodName());
+            
+            support.doContinue();
+            support.waitState(JPDADebugger.STATE_DISCONNECTED);
+        } finally {
+            if (support != null) {
+                support.doFinish();
+            }
+        }
+    }
+
+}
diff --git a/java/debugger.jpda.truffle/test/unit/src/org/netbeans/modules/debugger/jpda/truffle/RemoteServicesNGTest.java b/java/debugger.jpda.truffle/test/unit/src/org/netbeans/modules/debugger/jpda/truffle/RemoteServicesNGTest.java
new file mode 100644
index 0000000000..cb795fda92
--- /dev/null
+++ b/java/debugger.jpda.truffle/test/unit/src/org/netbeans/modules/debugger/jpda/truffle/RemoteServicesNGTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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 java.io.IOException;
+import java.io.InputStream;
+import java.util.jar.JarInputStream;
+import java.util.zip.ZipEntry;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+
+public class RemoteServicesNGTest {
+
+    public RemoteServicesNGTest() {
+    }
+
+    @Test
+    public void verifyTruffleBackendResourceExists() throws IOException {
+        final InputStream is = RemoteServices.openRemoteClasses();
+        assertNotNull(is);
+        JarInputStream jar = new JarInputStream(is);
+        for (;;) {
+            ZipEntry entry = jar.getNextEntry();
+            if (entry == null) {
+                fail("org.netbeans.modules.debugger.jpda.backend.truffle.JPDATruffleAccessor not found");
+            }
+            if (entry.getName().equals("org/netbeans/modules/debugger/jpda/backend/truffle/JPDATruffleAccessor.class")) {
+                // OK
+                return;
+            }
+        }
+    }
+
+}
diff --git a/java/debugger.jpda.truffle/test/unit/src/org/netbeans/modules/debugger/jpda/truffle/testapps/SLApp.java b/java/debugger.jpda.truffle/test/unit/src/org/netbeans/modules/debugger/jpda/truffle/testapps/SLApp.java
new file mode 100644
index 0000000000..15cabd43e8
--- /dev/null
+++ b/java/debugger.jpda.truffle/test/unit/src/org/netbeans/modules/debugger/jpda/truffle/testapps/SLApp.java
@@ -0,0 +1,65 @@
+/*
+ * 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.testapps;
+
+import java.io.ByteArrayOutputStream;
+
+import org.graalvm.polyglot.Context;
+import org.graalvm.polyglot.Source;
+import org.graalvm.polyglot.Value;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class SLApp {
+    public static void main(String... args) throws Exception {
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+        Source src = Source.newBuilder("sl",
+            "function main() {\n" +
+            "  x = 42;\n" +
+            "  println(x);\n" +
+            "  return x;\n" +
+            "}\n"+
+            "function init() {\n"+
+            "  obj = new();\n"+
+            "  obj.fourtyTwo = main;\n"+
+            "  return obj;\n"+
+            "}\n",
+            "Meaning of world.sl").build();
+        
+        Context context = Context.newBuilder().out(os).build();
+        Value result = context.eval(src);                           // LBREAKPOINT
+
+        assertEquals("Expected result", 42L, result.asLong());
+        assertEquals("Expected output", "42\n", os.toString("UTF-8"));
+        
+        // dynamic generated interface
+        Value init = context.getBindings("sl").getMember("init");
+        assertNotNull("init method found", init);
+        Compute c = init.execute().as(Compute.class);                           // LBREAKPOINT
+        Object result42 = c.fourtyTwo();                                        // LBREAKPOINT
+        assertEquals("Expected result", 42L, result42);
+        assertEquals("Expected output", "42\n42\n", os.toString("UTF-8"));
+    }
+    
+    public static interface Compute {
+        public Number fourtyTwo();
+    }
+}
diff --git a/java/debugger.jpda.truffle/test/unit/src/org/netbeans/modules/debugger/jpda/truffle/testapps/SLAppFromFile.java b/java/debugger.jpda.truffle/test/unit/src/org/netbeans/modules/debugger/jpda/truffle/testapps/SLAppFromFile.java
new file mode 100644
index 0000000000..e50a3f6f07
--- /dev/null
+++ b/java/debugger.jpda.truffle/test/unit/src/org/netbeans/modules/debugger/jpda/truffle/testapps/SLAppFromFile.java
@@ -0,0 +1,43 @@
+/*
+ * 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.testapps;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+
+import org.graalvm.polyglot.Context;
+import org.graalvm.polyglot.Source;
+import org.graalvm.polyglot.Value;
+import static org.junit.Assert.assertEquals;
+
+public class SLAppFromFile {
+    public static void main(String... args) throws Exception {
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        Context context = Context.newBuilder().out(os).build();
+
+        String path = args[0];
+        Source src = Source.newBuilder("sl", new File(path)).build();
+        
+        Value result = context.eval(src); // LBREAKPOINT
+
+        assertEquals("Expected result", 42L, result.asLong());
+        assertEquals("Expected output", "42\n", os.toString("UTF-8"));
+    }
+}
diff --git a/java/debugger.jpda.truffle/test/unit/src/org/netbeans/modules/debugger/jpda/truffle/testapps/TestApp.sl b/java/debugger.jpda.truffle/test/unit/src/org/netbeans/modules/debugger/jpda/truffle/testapps/TestApp.sl
new file mode 100644
index 0000000000..6d1100675f
--- /dev/null
+++ b/java/debugger.jpda.truffle/test/unit/src/org/netbeans/modules/debugger/jpda/truffle/testapps/TestApp.sl
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+function main() {
+    x = 42;
+    println(x); // LBREAKPOINT
+    return x;
+}
\ No newline at end of file
diff --git a/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/DebuggerVisualizer.java b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/DebuggerVisualizer.java
new file mode 100644
index 0000000000..47c2b05837
--- /dev/null
+++ b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/DebuggerVisualizer.java
@@ -0,0 +1,58 @@
+/*
+ * 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.backend.truffle;
+
+import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.RootCallTarget;
+import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.source.SourceSection;
+
+/**
+ *
+ * @author Martin
+ */
+final class DebuggerVisualizer {
+    
+    private DebuggerVisualizer() {}
+    
+    static String getDisplayName(CallTarget ct) {
+        if (ct instanceof RootCallTarget) {
+            RootNode rn = ((RootCallTarget) ct).getRootNode();
+            return getMethodName(rn);
+        } else {
+            //System.err.println("Unexpected CallTarget: "+ct.getClass());
+            return ct.toString();
+        }
+    }
+    
+    static String getMethodName(RootNode rn) {
+        return rn.getName();
+    }
+    
+    /** &lt;File name&gt;:&lt;line number&gt; */
+    static String getSourceLocation(SourceSection ss) {
+        if (ss == null) {
+            //System.err.println("No source section for node "+n);
+            return "unknown";
+        }
+        return ss.getSource().getName() + ":" + ss.getStartLine();
+    }
+
+}
diff --git a/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/FrameInfo.java b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/FrameInfo.java
new file mode 100644
index 0000000000..7e53dea10d
--- /dev/null
+++ b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/FrameInfo.java
@@ -0,0 +1,89 @@
+/*
+ * 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.backend.truffle;
+
+import com.oracle.truffle.api.debug.DebugStackFrame;
+import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.source.SourceSection;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+
+/**
+ * Collects stack frame information.
+ * 
+ * @author martin
+ */
+final class FrameInfo {
+    final DebugStackFrame frame;  // the top frame instance
+    final DebugStackFrame[] stackTrace; // All but the top frame
+    final String topFrame;
+    final Object[] topVariables;
+    // TODO: final Object[] thisObjects;
+
+    FrameInfo(DebugStackFrame topStackFrame, Iterable<DebugStackFrame> stackFrames) {
+        SourceSection topSS = topStackFrame.getSourceSection();
+        SourcePosition position = JPDATruffleDebugManager.getPosition(topSS);
+        ArrayList<DebugStackFrame> stackFramesArray = new ArrayList<>();
+        for (DebugStackFrame sf : stackFrames) {
+            if (sf == topStackFrame) {
+                continue;
+            }
+            SourceSection ss = sf.getSourceSection();
+            // Ignore frames without sources:
+            if (ss == null || ss.getSource() == null) {
+                continue;
+            }
+            stackFramesArray.add(sf);
+        }
+        frame = topStackFrame;
+        stackTrace = stackFramesArray.toArray(new DebugStackFrame[stackFramesArray.size()]);
+        topFrame = topStackFrame.getName() + "\n" +
+                   DebuggerVisualizer.getSourceLocation(topSS) + "\n" +
+                   position.id + "\n" + position.name + "\n" + position.path + "\n" +
+                   position.uri.toString() + "\n" + position.line + "\n" + isInternal(topStackFrame);
+        topVariables = JPDATruffleAccessor.getVariables(topStackFrame);
+    }
+    
+    /** Calls DebugStackFrame.isInternal() with workarounds for NPEs */
+    static boolean isInternal(DebugStackFrame sf) {
+        boolean isInternal = false;
+        try {
+            isInternal = sf.isInternal();
+        } catch (Exception ex) {
+            LangErrors.exception("Frame "+sf.getName()+" .isInternal()", ex);
+            //System.err.println("Is Internal blew up for "+sf+", name = "+sf.getName()+", source = "+DebuggerVisualizer.getSourceLocation(sf.getSourceSection()));
+            //System.err.println("  source section = "+sf.getSourceSection());
+            try {
+                Method findCurrentRootMethod = DebugStackFrame.class.getDeclaredMethod("findCurrentRoot");
+                findCurrentRootMethod.setAccessible(true);
+                RootNode rn = (RootNode) findCurrentRootMethod.invoke(sf);
+                //System.err.println("  root node = "+rn);
+                //System.err.println("  source section = "+rn.getSourceSection());
+                isInternal = rn.getSourceSection() == null;
+            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException |
+                     NoSuchMethodException | SecurityException ex2) {
+                LangErrors.exception("Frame "+sf.getName()+" findCurrentRoot() invocation", ex2);
+            }
+        }
+        return isInternal;
+    }
+    
+}
diff --git a/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/GetMIMETypes.java b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/GetMIMETypes.java
new file mode 100644
index 0000000000..08a923fe5f
--- /dev/null
+++ b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/GetMIMETypes.java
@@ -0,0 +1,67 @@
+/*
+ * 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.backend.truffle;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Stream;
+import org.graalvm.polyglot.Engine;
+
+/**
+ * Provides the MIME types of languages supported by a GraalVM instance.
+ * 
+ * @author martin
+ */
+public class GetMIMETypes {
+    
+    public static void main(String[] args) throws Exception {
+        /*
+        Method loadLanguageClass = Engine.class.getDeclaredMethod("loadLanguageClass", String.class);
+        loadLanguageClass.setAccessible(true);
+        Class polyglotEngineClass = (Class) loadLanguageClass.invoke(null, "com.oracle.truffle.api.vm.PolyglotEngine");
+        Object builder = polyglotEngineClass.getDeclaredMethod("newBuilder").invoke(null);
+        Object polyglotEngine = builder.getClass().getMethod("build").invoke(builder);
+        Map languages = (Map) polyglotEngine.getClass().getMethod("getLanguages").invoke(polyglotEngine);
+        languages.values().stream().
+                flatMap((l) -> {
+                    Stream<String> stream;
+                    try {
+                        stream = ((Set<String>) l.getClass().getMethod("getMimeTypes").invoke(l)).stream();
+                    } catch (Exception e) {
+                        throw new RuntimeException(e);
+                    }
+                    return stream;}).distinct().
+                forEach((mt) -> System.out.println(mt));
+        */
+        Engine.create().getLanguages().values().stream().
+                flatMap((l) -> l.getMimeTypes().stream()).distinct().
+                forEach((mt) -> System.out.println(mt));
+        /*
+        Set<String> MIMETypes = new HashSet<>();
+        Collection<? extends PolyglotEngine.Language> languages = PolyglotEngine.newBuilder().build().getLanguages().values();
+        for (PolyglotEngine.Language language : languages) {
+            MIMETypes.addAll(language.getMimeTypes());
+        }
+        for (String mt : MIMETypes) {
+            System.out.println(mt);
+        }*/
+    }
+}
diff --git a/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/GuestObject.java b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/GuestObject.java
new file mode 100644
index 0000000000..6d3f6a58c5
--- /dev/null
+++ b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/GuestObject.java
@@ -0,0 +1,236 @@
+/*
+ * 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.backend.truffle;
+
+import com.oracle.truffle.api.debug.DebugStackFrame;
+import com.oracle.truffle.api.debug.DebugValue;
+import com.oracle.truffle.api.source.SourceSection;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A guest language object.
+ */
+public final class GuestObject {
+    
+    static final int DISPLAY_TRIM = 1000;
+
+    final DebugValue value;
+    final String name;
+    final String type;
+    final String displayValue;
+    final boolean readable;
+    final boolean writable;
+    final boolean internal;
+    final boolean leaf;
+    final boolean isArray;
+    final Collection<DebugValue> properties;
+    final List<DebugValue> array;
+    final SourcePosition valueSourcePosition;
+    final SourcePosition typeSourcePosition;
+
+    GuestObject(DebugValue value) {
+        this.value = value;
+        this.name = value.getName();
+        //System.err.println("new GuestObject("+name+")");
+        DebugValue metaObject = null;
+        try {
+            metaObject = value.getMetaObject();
+        } catch (ThreadDeath td) {
+            throw td;
+        } catch (Throwable ex) {
+            LangErrors.exception("Value "+name+" .getMetaObject()", ex);
+        }
+        String typeStr = "";
+        if (metaObject != null) {
+            try {
+                typeStr = metaObject.as(String.class);
+            } catch (ThreadDeath td) {
+                throw td;
+            } catch (Throwable ex) {
+                LangErrors.exception("Meta object of "+name+" .as(String.class)", ex);
+            }
+        }
+        this.type = typeStr;
+        //this.object = value;
+        String valueStr = null;
+        try {
+            valueStr = value.as(String.class);
+        } catch (ThreadDeath td) {
+            throw td;
+        } catch (Throwable ex) {
+            LangErrors.exception("Value "+name+" .as(String.class)", ex);
+        }
+        if (valueStr == null) {
+            // Hack for R:
+            try {
+                Method getMethod = DebugValue.class.getDeclaredMethod("get");
+                getMethod.setAccessible(true);
+                Object realValue = getMethod.invoke(value);
+                valueStr = Objects.toString(realValue);
+            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException |
+                     NoSuchMethodException | SecurityException ex) {
+                LangErrors.exception("Value "+name+" get() invocation", ex);
+            }
+        }
+        this.displayValue = valueStr;
+        //System.err.println("  have display value "+valueStr);
+        this.readable = value.isReadable();
+        this.writable = value.isWritable();
+        this.internal = value.isInternal();
+        Collection<DebugValue> valueProperties;
+        try {
+            valueProperties = value.getProperties();
+        } catch (ThreadDeath td) {
+            throw td;
+        } catch (Throwable ex) {
+            LangErrors.exception("Value "+name+" .getProperties()", ex);
+            valueProperties = null;
+        }
+        this.properties = valueProperties;
+        //System.err.println("  have properties");
+        this.leaf = properties == null || properties.isEmpty();
+        boolean valueIsArray;
+        try {
+            valueIsArray = value.isArray();
+        } catch (ThreadDeath td) {
+            throw td;
+        } catch (Throwable ex) {
+            LangErrors.exception("Value "+name+" .isArray()", ex);
+            valueIsArray = false;
+        }
+        this.isArray = valueIsArray;
+        if (isArray) {
+            List<DebugValue> valueArray;
+            try {
+                valueArray = value.getArray();
+            } catch (ThreadDeath td) {
+                throw td;
+            } catch (Throwable ex) {
+                LangErrors.exception("Value "+name+" .getArray()", ex);
+                valueArray = null;
+            }
+            this.array = valueArray;
+        } else {
+            this.array = null;
+        }
+        SourcePosition sp = null;
+        try {
+            SourceSection sourceLocation = value.getSourceLocation();
+            //System.err.println("\nSOURCE of "+value.getName()+" is: "+sourceLocation);
+            if (sourceLocation != null) {
+                sp = JPDATruffleDebugManager.getPosition(sourceLocation);
+            }
+        } catch (ThreadDeath td) {
+            throw td;
+        } catch (Throwable ex) {
+            LangErrors.exception("Value "+name+" .getSourceLocation()", ex);
+        }
+        this.valueSourcePosition = sp;
+        sp = null;
+        if (metaObject != null) {
+            try {
+                SourceSection sourceLocation = metaObject.getSourceLocation();
+                //System.err.println("\nSOURCE of metaobject "+metaObject+" is: "+sourceLocation);
+                if (sourceLocation != null) {
+                    sp = JPDATruffleDebugManager.getPosition(sourceLocation);
+                }
+            } catch (ThreadDeath td) {
+                throw td;
+            } catch (Throwable ex) {
+                LangErrors.exception("Meta object of "+name+" .getSourceLocation()", ex);
+            }
+        }
+        this.typeSourcePosition = sp;
+        /*try {
+            System.err.println("new GuestObject("+name+") displayValue = "+displayValue+", leaf = "+leaf+", properties = "+properties);
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }*/
+    }
+
+    public GuestObject setValue(DebugStackFrame frame, String newExpression) {
+        DebugValue newValue;
+        try {
+            newValue = frame.eval(newExpression);
+        } catch (ThreadDeath td) {
+            throw td;
+        } catch (Throwable t) {
+            LangErrors.exception("Evaluation of '"+newExpression+"'", t);
+            return null;
+        }
+        try {
+            value.set(newValue);
+            return new GuestObject(value);
+        } catch (ThreadDeath td) {
+            throw td;
+        } catch (Throwable t) {
+            LangErrors.exception("Set of a value created from '"+newExpression+"'", t);
+            return null;
+        }
+    }
+
+    public GuestObject[] getProperties() {
+        if (properties == null) {
+            return new GuestObject[]{};
+        }
+        int n = 0;
+        try {
+            n = properties.size();
+        } catch (Exception ex) {
+            LangErrors.exception("Value "+name+" properties.size()", ex);
+        }
+        GuestObject[] children = new GuestObject[n];
+        if (n == 0) {
+            return children;
+        }
+        int i = 0;
+        for (DebugValue ch : properties) {
+            children[i++] = new GuestObject(ch);
+        }
+        return children;
+    }
+
+    public int getArraySize() {
+        return (array != null) ? array.size() : 0;
+    }
+
+    public GuestObject[] getArrayElements() {
+        int n = getArraySize();
+        GuestObject[] elements = new GuestObject[n];
+        if (n == 0) {
+            return elements;
+        }
+        int i = 0;
+        for (DebugValue elm : array) {
+            elements[i++] = new GuestObject(elm);
+        }
+        return elements;
+    }
+
+    @Override
+    public String toString() {
+        return name + " = " + displayValue;
+    }
+
+}
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
new file mode 100644
index 0000000000..26f624a0f3
--- /dev/null
+++ b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/JPDATruffleAccessor.java
@@ -0,0 +1,576 @@
+/*
+ * 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.backend.truffle;
+
+import com.oracle.truffle.api.debug.Breakpoint;
+import com.oracle.truffle.api.debug.DebugScope;
+import com.oracle.truffle.api.debug.DebugStackFrame;
+import com.oracle.truffle.api.debug.DebugValue;
+import com.oracle.truffle.api.debug.Debugger;
+import com.oracle.truffle.api.debug.DebuggerSession;
+import com.oracle.truffle.api.debug.SuspendedEvent;
+//import com.oracle.truffle.api.vm.PolyglotEngine;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+import org.graalvm.polyglot.Engine;
+
+/**
+ * Truffle accessor for JPDA debugger.
+ * 
+ * This class serves as a intermediary between the {@link JPDATruffleDebugManager}
+ * and JPDA Java debugger (<code>org.netbeans.modules.debugger.jpda.truffle.access.TruffleAccessor</code>),
+ * which submits breakpoints and calls methods of this class.
+ * To be able to invoke methods via debugger at any time, use {@link AccessLoop}.
+ * 
+ * Creation of a PolyglotEngine instance is out of control for the debugger.
+ * Thus to intercept execution and suspension, we add Java method breakpoints into
+ * <code>com.oracle.truffle.api.vm.PolyglotEngine.dispatchExecutionEvent()</code> and
+ * <code>com.oracle.truffle.api.vm.PolyglotEngine.dispatchSuspendedEvent()</code> methods.
+ * 
+ * @author Martin
+ */
+public class JPDATruffleAccessor extends Object {
+    
+    static final boolean TRACE = Boolean.getBoolean("truffle.nbdebug.trace");   // NOI18N
+    
+    private static final String ACCESS_THREAD_NAME = "JPDA Truffle Access Loop";   // NOI18N
+    private static volatile boolean accessLoopRunning = false;
+    private static volatile Thread accessLoopThread;
+    private static final Map<Debugger, JPDATruffleDebugManager> debugManagers = new WeakHashMap<>();
+    /** Explicitly set this field to true to step into script calls. */
+    static boolean isSteppingInto = false; // Step into was issued in JPDA debugger
+    static int steppingIntoTruffle = 0; // = 0 no stepping change, > 0 set step into, < 0 unset stepping into
+    /** A field to test for whether the access loop is sleeping and can be interrupted. */
+    static boolean accessLoopSleeping = false;
+    private static boolean stepIntoPrepared;
+
+    /** A step command:
+     * 0 no step (continue)
+     * 1 step into
+     * 2 step over
+     * 3 step out
+     */
+    //private static int stepCmd = 0;
+
+    public JPDATruffleAccessor() {}
+    
+    static Thread startAccessLoop() {
+        if (!accessLoopRunning) {
+            Thread loop;
+            AccessLoop accessLoop;
+            try {
+                accessLoop = new AccessLoop();
+                loop = new Thread(accessLoop, ACCESS_THREAD_NAME);
+                loop.setDaemon(true);
+                loop.setPriority(Thread.MIN_PRIORITY);
+            } catch (SecurityException se) {
+                return null;
+            }
+            accessLoopThread = loop;
+            accessLoopRunning = true;
+            loop.start();
+        }
+        return accessLoopThread;
+    }
+    
+    static void stopAccessLoop() {
+        synchronized (debugManagers) {
+            for (JPDATruffleDebugManager debugManager : debugManagers.values()) {
+                debugManager.dispose();
+            }
+        }
+        accessLoopRunning = false;
+        if (accessLoopThread != null) {
+            accessLoopThread.interrupt();
+            accessLoopThread = null;
+        }
+    }
+    
+    static JPDATruffleDebugManager setUpDebugManagerFor(/*Engine*/Object engineObj, boolean doStepInto) {
+        trace("setUpDebugManagerFor("+engineObj+", "+doStepInto+")");
+        Engine engine = (Engine) engineObj;
+        Debugger debugger;
+        try {
+            debugger = engine.getInstruments().get("debugger").lookup(Debugger.class);
+        } catch (NullPointerException npe) {
+            // An engine without instruments/debugger. E.g. Engine.EMPTY
+            return null;
+        }
+        synchronized (debugManagers) {
+            if (debugManagers.containsKey(debugger)) {
+                return null;
+            }
+        }
+        JPDATruffleDebugManager tdm = new JPDATruffleDebugManager(debugger, doStepInto);
+        synchronized (debugManagers) {
+            debugManagers.put(debugger, tdm);
+        }
+        return tdm;
+    }
+    
+    static int executionHalted(JPDATruffleDebugManager tdm,
+                               SourcePosition position,
+                               boolean haltedBefore,
+                               DebugValue returnValue,
+                               FrameInfo frameInfo,
+                               Breakpoint[] breakpointsHit,
+                               Throwable[] breakpointConditionExceptions,
+                               int stepCmd) {
+        // Called when the execution is halted. Have a breakpoint here.
+        return stepCmd;
+    }
+    
+    static void setStep(JPDATruffleDebugManager debugManager, int stepCmd) {
+        SuspendedEvent evt = debugManager.getCurrentSuspendedEvent();
+        switch (stepCmd) {
+            case 0: evt.prepareContinue();
+                    break;
+            case 1: evt.prepareStepInto(1);
+                    break;
+            case 2: evt.prepareStepOver(1);
+                    break;
+            case 3: evt.prepareStepOut(1);
+                    break;
+            default:
+                    throw new IllegalStateException("Unknown step command: "+stepCmd);
+        }
+    }
+    
+    static void suspendNextExecution() {
+        DebuggerSession[] sessions;
+        synchronized (debugManagers) {
+            sessions = new DebuggerSession[debugManagers.size()];
+            int i = 0;
+            for (JPDATruffleDebugManager tdm : debugManagers.values()) {
+                sessions[i++] = tdm.getDebuggerSession();
+            }
+        }
+        for (DebuggerSession session : sessions) {
+            session.suspendNextExecution();
+        }
+    }
+    
+    /**
+     * @param frames The array of stack frame infos
+     * @return An array of two elements: a String of frame information and
+     * an array of code contents.
+     */
+    static Object[] getFramesInfo(DebugStackFrame[] frames, boolean includeInternal) {
+        trace("getFramesInfo({0})",includeInternal);
+        int n = frames.length;
+        StringBuilder frameInfos = new StringBuilder();
+        String[] codes = new String[n];
+        Object[] thiss = new Object[n];
+        int j = 0;
+        for (int i = 0; i < n; i++) {
+            DebugStackFrame sf = frames[i];
+            boolean isInternal = FrameInfo.isInternal(sf);
+            //System.err.println("SF("+sf.getName()+", "+sf.getSourceSection()+") is internal = "+isInternal);
+            if (!includeInternal && isInternal) {
+                continue;
+            }
+            String sfName = sf.getName();
+            if (sfName == null) {
+                sfName = "";
+            }
+            frameInfos.append(sfName);
+            frameInfos.append('\n');
+            frameInfos.append(DebuggerVisualizer.getSourceLocation(sf.getSourceSection()));
+            frameInfos.append('\n');
+            /*if (fi.getCallNode() == null) {
+                /* frames with null call nodes are filtered out by JPDATruffleDebugManager.FrameInfo
+                System.err.println("Frame with null call node: "+fi);
+                System.err.println("  is virtual frame = "+fi.isVirtualFrame());
+                System.err.println("  call target = "+fi.getCallTarget());
+                System.err.println("frameInfos = "+frameInfos);
+                *//*
+            }*/
+            SourcePosition position = JPDATruffleDebugManager.getPosition(sf.getSourceSection());
+            frameInfos.append(createPositionIdentificationString(position));
+            if (includeInternal) {
+                frameInfos.append('\n');
+                frameInfos.append(isInternal);
+            }
+            
+            frameInfos.append("\n\n");
+            
+            codes[j] = position.code;
+            /*
+             TODO Find "this"
+            Frame f = fi.getFrame(FrameInstance.FrameAccess.READ_ONLY, false);
+            if (f instanceof VirtualFrame) {
+                thiss[i] = JSFrameUtil.getThisObj((VirtualFrame) f);
+            }*/
+            j++;
+        }
+        if (j < n) {
+            codes = Arrays.copyOf(codes, j);
+            thiss = Arrays.copyOf(thiss, j);
+        }
+        boolean areSkippedInternalFrames = j < n;
+        return new Object[] { frameInfos.toString(), codes, thiss, areSkippedInternalFrames };
+    }
+    
+    private static String createPositionIdentificationString(SourcePosition position) {
+        StringBuilder str = new StringBuilder();
+        str.append(position.id);
+        str.append('\n');
+        str.append(position.name);
+        str.append('\n');
+        str.append(position.path);
+        str.append('\n');
+        str.append(position.uri.toString());
+        str.append('\n');
+        str.append(position.line);
+        return str.toString();
+    }
+
+    static Object[] getTruffleAST(int depth) {
+        TruffleAST ast = TruffleAST.get(depth);
+        return new Object[] { ast.getNodes(), ast.getRawArguments(), ast.getRawSlots() };
+    }
+
+    // Unwind the current thread to given depth
+    static boolean setUnwind(int depth) {
+        SuspendedEvent evt = getCurrentSuspendedEvent();
+        if (evt == null) {
+            return false;
+        }
+        Iterator<DebugStackFrame> iterator = evt.getStackFrames().iterator();
+        DebugStackFrame frame = iterator.next();
+        while (depth > 0 && iterator.hasNext()) {
+            frame = iterator.next();
+            depth--;
+        }
+        if (depth != 0) {
+            return false;
+        }
+        evt.prepareUnwindFrame(frame);
+        return true;
+    }
+    
+    // An array of scopes and their variables:
+    // <scope name>, <is functional>, <num args>, <num vars>, [(num args)+(num vars) variables]
+    // Variable: 11 elements: <name>, <type>, <readable>, <writable>, <internal>, <String value>,
+    //                        <var source>, <VS code>, <type source>, <TS code>, <DebugValue>
+    // Parent scopes: <scope name>, <is functional>, <has args>, <has vars>, <DebugScope>
+    static Object[] getVariables(DebugStackFrame sf) {
+        List<Object> elements = new ArrayList<>();
+        try {
+            DebugScope scope = sf.getScope();
+            while (scope != null) {
+                Iterable<DebugValue> argsIt = scope.getArguments();
+                Iterator<DebugValue> args;
+                if (argsIt != null) {
+                    args = argsIt.iterator();
+                } else {
+                    args = null;
+                }
+                Iterable<DebugValue> varsIt = scope.getDeclaredValues();
+                Iterator<DebugValue> vars = varsIt.iterator();
+                if ((args == null || !args.hasNext()) && !vars.hasNext()) {
+                    // An empty scope, skip it
+                    scope = scope.getParent();
+                    continue;
+                }
+                elements.add(scope.getName());
+                elements.add(scope.isFunctionScope());
+                List<DebugValue> arguments = null;
+                if (args != null && args.hasNext()) {
+                    arguments = new ArrayList<>();
+                    while (args.hasNext()) {
+                        arguments.add(args.next());
+                    }
+                    elements.add(arguments.size());
+                } else {
+                    elements.add(0);
+                }
+                List<DebugValue> variables = new ArrayList<>();
+                while (vars.hasNext()) {
+                    variables.add(vars.next());
+                }
+                elements.add(variables.size());
+                if (arguments != null) {
+                    for (DebugValue v : arguments) {
+                        addValueElement(v, elements);
+                    }
+                }
+                for (DebugValue v : variables) {
+                    addValueElement(v, elements);
+                }
+                // We've filled the first scope in.
+                break;
+            }
+            
+            if (scope != null) {
+                while ((scope = scope.getParent()) != null) {
+                    elements.add(scope.getName());
+                    elements.add(scope.isFunctionScope());
+                    Iterable<DebugValue> args = scope.getArguments();
+                    boolean hasArgs = (args != null && args.iterator().hasNext());
+                    elements.add(hasArgs);
+                    boolean hasVars = scope.getDeclaredValues().iterator().hasNext();
+                    elements.add(hasVars);
+                    elements.add(scope);
+                }
+            }
+        } catch (ThreadDeath td) {
+            throw td;
+        } catch (Throwable t) {
+            LangErrors.exception("An error when accessing scopes", t);
+        }
+        return elements.toArray();
+    }
+
+    // An array of scope's arguments and variables:
+    // <num args>, <num vars>, [(num args)+(num vars) variables]
+    // Variable: 11 elements: <name>, <type>, <readable>, <writable>, <internal>, <String value>,
+    //                        <var source>, <VS code>, <type source>, <TS code>, <DebugValue>
+    static Object[] getScopeVariables(DebugScope scope) {
+        List<Object> elements = new ArrayList<>();
+        try {
+            Iterable<DebugValue> argsIt = scope.getArguments();
+            Iterator<DebugValue> args;
+            if (argsIt != null) {
+                args = argsIt.iterator();
+            } else {
+                args = null;
+            }
+            Iterable<DebugValue> varsIt = scope.getDeclaredValues();
+            Iterator<DebugValue> vars = varsIt.iterator();
+            List<DebugValue> arguments = null;
+            if (args != null && args.hasNext()) {
+                arguments = new ArrayList<>();
+                while (args.hasNext()) {
+                    arguments.add(args.next());
+                }
+                elements.add(arguments.size());
+            } else {
+                elements.add(0);
+            }
+            List<DebugValue> variables = new ArrayList<>();
+            while (vars.hasNext()) {
+                variables.add(vars.next());
+            }
+            elements.add(variables.size());
+            if (arguments != null) {
+                for (DebugValue v : arguments) {
+                    addValueElement(v, elements);
+                }
+            }
+            for (DebugValue v : variables) {
+                addValueElement(v, elements);
+            }
+        } catch (ThreadDeath td) {
+            throw td;
+        } catch (Throwable t) {
+            LangErrors.exception("An error when accessing scope "+scope, t);
+        }
+        return elements.toArray();
+    }
+
+    // Store 11 elements: <name>, <type>, <readable>, <writable>, <internal>, <String value>,
+    //                    <var source>, <VS code>, <type source>, <TS code>, <DebugValue>
+    static void addValueElement(DebugValue value, List<Object> elements) {
+        GuestObject tobj = new GuestObject(value);
+        elements.add(tobj.name);
+        elements.add(tobj.type);
+        elements.add(tobj.readable);
+        elements.add(tobj.writable);
+        elements.add(tobj.internal);
+        elements.add(tobj.displayValue);
+        if (tobj.valueSourcePosition != null) {
+            elements.add(createPositionIdentificationString(tobj.valueSourcePosition));
+            elements.add(tobj.valueSourcePosition.code);
+        } else {
+            elements.add(null);
+            elements.add(null);
+        }
+        if (tobj.typeSourcePosition != null) {
+            elements.add(createPositionIdentificationString(tobj.typeSourcePosition));
+            elements.add(tobj.typeSourcePosition.code);
+        } else {
+            elements.add(null);
+            elements.add(null);
+        }
+        elements.add(tobj);
+    }
+
+    static void debuggerAccess() {
+        // A breakpoint is submitted on this method.
+        // When accessLoopThread is interrupted, this breakpoint is hit
+        // and methods can be executed via JPDA debugger.
+    }
+    
+    static Breakpoint[] setLineBreakpoint(String uriStr, int line,
+                                          int ignoreCount, String condition) throws URISyntaxException {
+        return doSetLineBreakpoint(new URI(uriStr), line, ignoreCount, condition, false);
+    }
+    
+    static Breakpoint setLineBreakpoint(JPDATruffleDebugManager debugManager, String uriStr, int line,
+                                        int ignoreCount, String condition) throws URISyntaxException {
+        try {
+            return doSetLineBreakpoint(debugManager.getDebuggerSession(), new URI(uriStr), line, ignoreCount, condition, false);
+        } catch (IOException ex) {
+            System.err.println("setLineBreakpoint("+uriStr+", "+line+"): "+ex);
+            return null;
+        }
+    }
+    
+    static Breakpoint[] setOneShotLineBreakpoint(String uriStr, int line) throws URISyntaxException {
+        return doSetLineBreakpoint(new URI(uriStr), line, 0, null, true);
+    }
+    
+    private static Breakpoint[] doSetLineBreakpoint(URI uri, int line,
+                                                    int ignoreCount, String condition,
+                                                    boolean oneShot) {
+        Breakpoint[] lbs;
+        JPDATruffleDebugManager[] managers;
+        synchronized (debugManagers) {
+            managers = debugManagers.values().toArray(new JPDATruffleDebugManager[] {});
+        }
+        lbs = new Breakpoint[managers.length];
+        int i = 0;
+        for (JPDATruffleDebugManager debugManager : managers) {
+            DebuggerSession debuggerSession = debugManager.getDebuggerSession();
+            if (debuggerSession == null) {
+                lbs = Arrays.copyOf(lbs, lbs.length - 1);
+                //synchronized (debugManagers) {
+                //    debugManagers.remove(debugger);
+                //}
+                continue;
+            }
+            Breakpoint lb;
+            try {
+                lb = doSetLineBreakpoint(debuggerSession, uri, line,
+                                         ignoreCount, condition, oneShot);
+            } catch (IOException dex) {
+                System.err.println("setLineBreakpoint("+uri+", "+line+"): "+dex);
+                lbs = Arrays.copyOf(lbs, lbs.length - 1);
+                continue;
+            }
+            lbs[i++] = lb;
+        }
+        return lbs;
+    }
+    
+    private static Breakpoint doSetLineBreakpoint(DebuggerSession debuggerSession,
+                                                  URI uri, int line,
+                                                  int ignoreCount, String condition,
+                                                  boolean oneShot) throws IOException {
+        Breakpoint.Builder bb = Breakpoint.newBuilder(uri).lineIs(line);
+        if (ignoreCount != 0) {
+            bb.ignoreCount(ignoreCount);
+        }
+        if (oneShot) {
+            bb.oneShot();
+        }
+        Breakpoint lb = bb.build();
+        if (condition != null) {
+            lb.setCondition(condition);
+        }
+        trace("JPDATruffleAccessor.setLineBreakpoint({0}, {1}, {2}): lb = {3}", debuggerSession, uri, line, lb);
+        return debuggerSession.install(lb);
+    }
+    
+    static void removeBreakpoint(Object br) {
+        ((Breakpoint) br).dispose();
+    }
+    
+    static Object evaluate(DebugStackFrame sf, String expression) {
+        DebugValue value = sf.eval(expression);
+        return new GuestObject(value);
+    }
+    
+    /** Get the suspended event on current thread, if any. */
+    private static SuspendedEvent getCurrentSuspendedEvent() {
+        synchronized (debugManagers) {
+            for (JPDATruffleDebugManager tdm : debugManagers.values()) {
+                SuspendedEvent evt = tdm.getCurrentSuspendedEvent();
+                if (evt != null) {
+                    return evt;
+                }
+            }
+        }
+        return null;
+    }
+    
+    static void trace(String message, Object... parameters) {
+        if (TRACE) {
+            System.out.println("NB Debugger: " + MessageFormat.format(message, parameters));
+        }
+    }
+
+    private static class AccessLoop implements Runnable {
+        
+        @Override
+        public void run() {
+            while (accessLoopRunning) {
+                accessLoopSleeping = true;
+                // Wait until we're interrupted
+                try {
+                    Thread.sleep(Long.MAX_VALUE);
+                } catch (InterruptedException iex) {}
+                accessLoopSleeping = false;
+                trace("AccessLoop: steppingIntoTruffle = "+steppingIntoTruffle+", isSteppingInto = "+isSteppingInto+", stepIntoPrepared = "+stepIntoPrepared);
+                
+                if (steppingIntoTruffle != 0) {
+                    if (steppingIntoTruffle > 0) {
+                        if (!stepIntoPrepared) {
+                            synchronized (debugManagers) {
+                                for (JPDATruffleDebugManager debugManager : debugManagers.values()) {
+                                    debugManager.prepareExecStepInto();
+                                }
+                            }
+                            stepIntoPrepared = true;
+                            trace("Prepared step into and continue.");
+                        }
+                        isSteppingInto = true;
+                    } else {
+                        // un-prepare step into, if possible.
+                        synchronized (debugManagers) {
+                            for (JPDATruffleDebugManager debugManager : debugManagers.values()) {
+                                debugManager.prepareExecContinue();
+                            }
+                        }
+                        isSteppingInto = false;
+                        stepIntoPrepared = false;
+                    }
+                    steppingIntoTruffle = 0;
+                    continue;
+                }
+                trace("accessLoopRunning = "+accessLoopRunning+", possible debugger access...");
+                if (accessLoopRunning) {
+                    debuggerAccess();
+                }
+            }
+        }
+
+    }
+
+}
diff --git a/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/JPDATruffleDebugManager.java b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/JPDATruffleDebugManager.java
new file mode 100644
index 0000000000..98ecfa7746
--- /dev/null
+++ b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/JPDATruffleDebugManager.java
@@ -0,0 +1,146 @@
+/*
+ * 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.backend.truffle;
+
+import com.oracle.truffle.api.debug.Breakpoint;
+import com.oracle.truffle.api.debug.Debugger;
+import com.oracle.truffle.api.debug.DebuggerSession;
+import com.oracle.truffle.api.debug.SuspendAnchor;
+import com.oracle.truffle.api.debug.SuspendedCallback;
+import com.oracle.truffle.api.debug.SuspendedEvent;
+import com.oracle.truffle.api.debug.SuspensionFilter;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.source.SourceSection;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+
+/**
+ *
+ * @author martin
+ */
+class JPDATruffleDebugManager implements SuspendedCallback {
+    
+    private final Reference<Debugger> debugger;
+    private final Reference<DebuggerSession> session;
+    private final ThreadLocal<SuspendedEvent> suspendedEvents = new ThreadLocal<>();
+
+    public JPDATruffleDebugManager(Debugger debugger, boolean doStepInto) {
+        this.debugger = new WeakReference<>(debugger);
+        DebuggerSession debuggerSession = debugger.startSession(this);
+        debuggerSession.setSteppingFilter(SuspensionFilter.newBuilder().ignoreLanguageContextInitialization(true).build());
+        if (doStepInto) {
+            debuggerSession.suspendNextExecution();
+        }
+        this.session = new WeakReference<>(debuggerSession);
+    }
+    
+    Debugger getDebugger() {
+        return debugger.get();
+    }
+    
+    DebuggerSession getDebuggerSession() {
+        return session.get();
+    }
+    
+    SuspendedEvent getCurrentSuspendedEvent() {
+        return suspendedEvents.get();
+    }
+    
+    static SourcePosition getPosition(SourceSection sourceSection) {
+        int line = sourceSection.getStartLine();
+        Source source = sourceSection.getSource();
+        String name = source.getName();
+        String path = source.getPath();
+        if (path == null) {
+            path = name;
+        }
+        String code = source.getCharacters().toString();
+        return new SourcePosition(source, name, path, line, code);
+    }
+
+    void dispose() {
+        DebuggerSession ds = session.get();
+        if (ds != null) {
+            ds.close();
+            session.clear();
+        }
+    }
+    
+    void prepareExecStepInto() {
+        session.get().suspendNextExecution();
+    }
+
+    void prepareExecContinue() {
+        //System.err.println("prepareExecContinue()...");
+        // TODO: HOW?
+        //prepareStepInto = false;
+        // Do not call methods on ExecutionEvent asynchronously.
+        /* Rely on another ExecutionEvent comes when needed
+        try {
+            execEvent.prepareContinue();
+        } catch (RuntimeException rex) {
+            // Unable to use the event any more. A new should come when needed.
+            // Report until there is some known contract:
+            System.err.println("Ignoring prepareExecContinue():");
+            rex.printStackTrace();
+        }
+        */
+        //System.err.println("prepareExecContinue() DONE.");
+    }
+
+    @Override
+    public void onSuspend(SuspendedEvent event) {
+        JPDATruffleAccessor.trace("JPDATruffleDebugManager.onSuspend({0})", event);
+        Breakpoint[] breakpointsHit = new Breakpoint[event.getBreakpoints().size()];
+        breakpointsHit = event.getBreakpoints().toArray(breakpointsHit);
+        Throwable[] breakpointConditionExceptions = new Throwable[breakpointsHit.length];
+        for (int i = 0; i < breakpointsHit.length; i++) {
+            breakpointConditionExceptions[i] = event.getBreakpointConditionException(breakpointsHit[i]);
+        }
+        suspendedEvents.set(event);
+        try {
+            SourcePosition position = getPosition(event.getSourceSection());
+            int stepCmd = JPDATruffleAccessor.executionHalted(
+                    this, position,
+                    event.getSuspendAnchor() == SuspendAnchor.BEFORE,
+                    event.getReturnValue(),
+                    new FrameInfo(event.getTopStackFrame(), event.getStackFrames()),
+                    breakpointsHit,
+                    breakpointConditionExceptions,
+                    0);
+            switch (stepCmd) {
+                case -1: break;
+                case 0: event.prepareContinue();
+                        break;
+                case 1: event.prepareStepInto(1);
+                        break;
+                case 2: event.prepareStepOver(1);
+                        break;
+                case 3: event.prepareStepOut(1);
+                        break;
+                default:
+                        throw new IllegalStateException("Unknown step command: "+stepCmd);
+            }
+        } finally {
+            suspendedEvents.remove();
+        }
+    }
+    
+}
diff --git a/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/LangErrors.java b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/LangErrors.java
new file mode 100644
index 0000000000..2a10327e31
--- /dev/null
+++ b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/LangErrors.java
@@ -0,0 +1,39 @@
+/*
+ * 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.backend.truffle;
+
+/**
+ * Errors in a language implementation.
+ * 
+ * @author martin
+ */
+final class LangErrors {
+    
+    private static final boolean SUPRESS_EXCEPTIONS = Boolean.getBoolean("truffle.nbdebug.supressLangErrs");
+    
+    private LangErrors() {}
+    
+    static void exception(String context, Throwable ex) {
+        if (!SUPRESS_EXCEPTIONS) {
+            System.err.println(context);
+            ex.printStackTrace();
+        }
+    }
+}
diff --git a/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/SourcePosition.java b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/SourcePosition.java
new file mode 100644
index 0000000000..5552ba6658
--- /dev/null
+++ b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/SourcePosition.java
@@ -0,0 +1,61 @@
+/*
+ * 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.backend.truffle;
+
+import com.oracle.truffle.api.source.Source;
+import java.net.URI;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+/**
+ *
+ * @author Martin
+ */
+final class SourcePosition {
+
+    private static final Map<Source, Long> sourceId = new WeakHashMap<>();
+    private static long nextId = 0;
+
+    final long id;
+    final String name;
+    final String path;
+    final int line;
+    final String code;
+    final URI uri;
+
+    public SourcePosition(Source source, String name, String path, int line, String code) {
+        this.id = getId(source);
+        this.name = name;
+        this.path = path;
+        this.line = line;
+        this.code = code;
+        this.uri = source.getURI();
+    }
+
+    private static synchronized long getId(Source s) {
+        Long id = sourceId.get(s);
+        if (id == null) {
+            id = new Long(nextId++);
+            sourceId.put(s, id);
+        }
+        return id;
+    }
+
+}
diff --git a/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/TruffleAST.java b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/TruffleAST.java
new file mode 100644
index 0000000000..997e64f380
--- /dev/null
+++ b/java/debugger.jpda.truffle/truffle-backend/org/netbeans/modules/debugger/jpda/backend/truffle/TruffleAST.java
@@ -0,0 +1,165 @@
+/*
+ * 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.backend.truffle;
+
+import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.RootCallTarget;
+import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.frame.Frame;
+import com.oracle.truffle.api.frame.FrameInstance;
+import com.oracle.truffle.api.frame.FrameInstanceVisitor;
+import com.oracle.truffle.api.frame.FrameSlot;
+import com.oracle.truffle.api.instrumentation.StandardTags;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeUtil;
+import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.source.SourceSection;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ *
+ * @author martin
+ */
+public class TruffleAST {
+
+    private final FrameInstance frameInstance;
+
+    private TruffleAST(FrameInstance frameInstance) {
+        this.frameInstance = frameInstance;
+    }
+
+    static TruffleAST get(int depth) {
+        final AtomicInteger frameDepth = new AtomicInteger(depth);
+        FrameInstance frameInstance = Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor<FrameInstance>() {
+            @Override
+            public FrameInstance visitFrame(FrameInstance frameInstance) {
+                CallTarget callTarget = frameInstance.getCallTarget();
+                if (!(callTarget instanceof RootCallTarget)) {
+                    return null;
+                }
+                RootCallTarget rct = (RootCallTarget) callTarget;
+                SourceSection rootSourceSection = rct.getRootNode().getSourceSection();
+                if (rootSourceSection != null) {
+                    if (frameDepth.getAndDecrement() == 0) {
+                        return frameInstance;
+                    }
+                }
+                return null;
+            }
+        });
+        return frameInstance == null ? null : new TruffleAST(frameInstance);
+    }
+
+    public Object[] getRawArguments() {
+        return frameInstance.getFrame(FrameInstance.FrameAccess.MATERIALIZE).getArguments();
+    }
+
+    public Object[] getRawSlots() {
+        Frame frame = frameInstance.getFrame(FrameInstance.FrameAccess.MATERIALIZE);
+        List<? extends FrameSlot> slots = frame.getFrameDescriptor().getSlots();
+        int n = slots.size();
+        Object[] slotInfo = new Object[2*n];
+        for (int i = 0; i < n; i++) {
+            FrameSlot slot = slots.get(i);
+            slotInfo[2*i] = slot.getIdentifier();
+            slotInfo[2*i + 1] = frame.getValue(slot);
+        }
+        return slotInfo;
+    }
+
+    /**
+     * Get the nodes hierarchy. Every node is described by:
+     * <ul>
+     *  <li>node class</li>
+     *  <li>node description</li>
+     *  <li>node source section - either an empty line, or following items:</li>
+     *  <ul>
+     *   <li>URI</li>
+     *   <li>&lt;start line&gt;:&lt;start column&gt;-&lt;end line&gt;:&lt;end column&gt;</li>
+     *  </ul>
+     *  <li>number of children</li>
+     *  <li>&lt;child nodes follow...&gt;</li>
+     * </ul>
+     * @return a newline-separated list of elements describing the nodes hierarchy.
+     */
+    public String getNodes() {
+        StringBuilder nodes = new StringBuilder();
+        RootCallTarget rct = (RootCallTarget) frameInstance.getCallTarget();
+        RootNode rootNode = rct.getRootNode();
+        fillNode(rootNode, nodes);
+        return nodes.toString();
+    }
+
+    private static void fillNode(Node node, StringBuilder nodes) {
+        nodes.append(node.getClass().getName());
+        nodes.append('\n');
+        nodes.append(node.getDescription());
+        nodes.append('\n');
+        SourceSection ss = node.getSourceSection();
+        if (ss == null) {
+            nodes.append('\n');
+        } else {
+            nodes.append(ss.getSource().getURI().toString());
+            nodes.append('\n');
+            nodes.append(Integer.toString(ss.getStartLine()));
+            nodes.append(':');
+            nodes.append(Integer.toString(ss.getStartColumn()));
+            nodes.append('-');
+            nodes.append(Integer.toString(ss.getEndLine()));
+            nodes.append(':');
+            nodes.append(Integer.toString(ss.getEndColumn()));
+            nodes.append('\n');
+            //nodes.add(ss.getCode());
+        }
+        // TAGS:
+        try {
+            java.lang.reflect.Method isTaggedWithMethod = Node.class.getDeclaredMethod("isTaggedWith", Class.class);
+            isTaggedWithMethod.setAccessible(true);
+            StringBuilder tags = new StringBuilder();
+            if ((Boolean) isTaggedWithMethod.invoke(node, StandardTags.RootTag.class)) {
+                tags.append("Root");
+            }
+            if ((Boolean) isTaggedWithMethod.invoke(node, StandardTags.CallTag.class)) {
+                if (tags.length() > 0) {
+                    tags.append(' ');
+                }
+                tags.append("Call");
+            }
+            if ((Boolean) isTaggedWithMethod.invoke(node, StandardTags.StatementTag.class)) {
+                if (tags.length() > 0) {
+                    tags.append(' ');
+                }
+                tags.append("Statement");
+            }
+            nodes.append(tags);
+        } catch (Throwable t) {
+        }
+        nodes.append('\n');
+        List<Node> ch = NodeUtil.findNodeChildren(node);
+        nodes.append(Integer.toString(ch.size()));
+        nodes.append('\n');
+        for (Node n : ch) {
+            fillNode(n, nodes);
+        }
+    }
+    
+}
diff --git a/java/debugger.jpda.trufflenode/build.xml b/java/debugger.jpda.trufflenode/build.xml
new file mode 100644
index 0000000000..b512f1b8ae
--- /dev/null
+++ b/java/debugger.jpda.trufflenode/build.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<project basedir="." default="netbeans" name="java/debugger.jpda.trufflenode">
+    <import file="../../nbbuild/templates/projectized.xml"/>
+</project>
diff --git a/java/debugger.jpda.trufflenode/manifest.mf b/java/debugger.jpda.trufflenode/manifest.mf
new file mode 100644
index 0000000000..46be319eb3
--- /dev/null
+++ b/java/debugger.jpda.trufflenode/manifest.mf
@@ -0,0 +1,7 @@
+Manifest-Version: 1.0
+AutoUpdate-Show-In-Client: false
+OpenIDE-Module: org.netbeans.modules.debugger.jpda.trufflenode/1
+OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/debugger/jpda/truffle/node/Bundle.properties
+OpenIDE-Module-Specification-Version: 1.0
+OpenIDE-Module-Recommends: org.netbeans.modules.debugger.jpda.truffle
+OpenIDE-Module-Package-Dependencies: com.sun.jdi[VirtualMachineManager]
diff --git a/java/debugger.jpda.trufflenode/nbproject/project.properties b/java/debugger.jpda.trufflenode/nbproject/project.properties
new file mode 100644
index 0000000000..ea0391e22b
--- /dev/null
+++ b/java/debugger.jpda.trufflenode/nbproject/project.properties
@@ -0,0 +1,24 @@
+# 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.
+
+is.eager=true
+javac.compilerargs=-Xlint:unchecked
+javac.source=1.7
+javadoc.arch=${basedir}/arch.xml
+nbm.module.author=Martin Entlicher
+requires.nb.javac=true
+cp.extra=${tools.jar}
diff --git a/java/debugger.jpda.trufflenode/nbproject/project.xml b/java/debugger.jpda.trufflenode/nbproject/project.xml
new file mode 100644
index 0000000000..2f0f47fcce
--- /dev/null
+++ b/java/debugger.jpda.trufflenode/nbproject/project.xml
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<project xmlns="http://www.netbeans.org/ns/project/1">
+    <type>org.netbeans.modules.apisupport.project</type>
+    <configuration>
+        <data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
+            <code-name-base>org.netbeans.modules.debugger.jpda.trufflenode</code-name-base>
+            <module-dependencies>
+                <dependency>
+                    <code-name-base>org.netbeans.api.debugger</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.54</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.api.debugger.jpda</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>2</release-version>
+                        <specification-version>3.5</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.api.java.classpath</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.52</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.extexecution</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>2</release-version>
+                        <specification-version>1.48</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.java.j2seplatform</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.41</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.java.platform</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.40</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.javascript.nodejs</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>0-1</release-version>
+                        <specification-version>0.31</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.projectapi</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.66</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.filesystems</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>9.10</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.io</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>1.49</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.util</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>9.7</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.util.lookup</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>8.33</specification-version>
+                    </run-dependency>
+                </dependency>
+            </module-dependencies>
+            <public-packages/>
+        </data>
+    </configuration>
+</project>
diff --git a/java/debugger.jpda.trufflenode/src/org/netbeans/modules/debugger/jpda/truffle/node/Bundle.properties b/java/debugger.jpda.trufflenode/src/org/netbeans/modules/debugger/jpda/truffle/node/Bundle.properties
new file mode 100644
index 0000000000..5941f7fa32
--- /dev/null
+++ b/java/debugger.jpda.trufflenode/src/org/netbeans/modules/debugger/jpda/truffle/node/Bundle.properties
@@ -0,0 +1,22 @@
+# 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.
+
+# module description
+OpenIDE-Module-Name=GraalVM Node.js Debugging Support
+OpenIDE-Module-Display-Category=Web
+OpenIDE-Module-Short-Description=Supports Node.js Debugging on GraalVM
+OpenIDE-Module-Long-Description=Supports debugging of Node.js based on top of GraalVM.
diff --git a/java/debugger.jpda.trufflenode/src/org/netbeans/modules/debugger/jpda/truffle/node/GraalVmStartupExtender.java b/java/debugger.jpda.trufflenode/src/org/netbeans/modules/debugger/jpda/truffle/node/GraalVmStartupExtender.java
new file mode 100644
index 0000000000..bc4116982a
--- /dev/null
+++ b/java/debugger.jpda.trufflenode/src/org/netbeans/modules/debugger/jpda/truffle/node/GraalVmStartupExtender.java
@@ -0,0 +1,96 @@
+/*
+ * 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.node;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.netbeans.api.extexecution.startup.StartupExtender;
+import org.netbeans.api.project.Project;
+import org.netbeans.modules.javascript.nodejs.api.NodeJsSupport;
+import org.netbeans.spi.extexecution.startup.StartupExtenderImplementation;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.util.BaseUtilities;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+import org.openide.windows.IOProvider;
+import org.openide.windows.InputOutput;
+
+@NbBundle.Messages("DESC_DebugGraalNode=Debug GraalVM Node.js")
+@StartupExtenderImplementation.Registration(displayName = "#DESC_DebugGraalNode", startMode = StartupExtender.StartMode.DEBUG, position=500)
+public class GraalVmStartupExtender implements StartupExtenderImplementation {
+
+    @NbBundle.Messages("CTL_DebugName=GraalVM node Debugger")
+    @Override
+    public List<String> getArguments(Lookup context, StartupExtender.StartMode mode) {
+        Project p = context.lookup(Project.class);
+        if (p == null) {
+            return Collections.emptyList();
+        }
+        NodeJsSupport s = NodeJsSupport.getInstance();
+        if (!s.isEnabled(p)) {
+            return Collections.emptyList();
+        }
+        if (mode != StartupExtender.StartMode.DEBUG) {
+            return Collections.emptyList();
+        }
+        final String node = s.getNode(p);
+        File nodeFile = new File(node);
+        nodeFile = FileUtil.normalizeFile(nodeFile);
+        FileObject nodeFO = FileUtil.toFileObject(nodeFile);
+        if (nodeFO == null) {
+            return Collections.emptyList();
+        }
+        FileObject bin = nodeFO.getParent();
+        if (bin == null || !isJavaPlatformBinDir(bin)) {
+            return Collections.emptyList();
+        }
+        final String debugName = Bundle.CTL_DebugName();
+
+        InputOutput io = IOProvider.getDefault().getIO(debugName, false);
+        FileObject jdk = bin.getParent();
+        if (jdk.getName().equals("jre")) {
+            jdk = jdk.getParent();
+        }
+        JPDAStart start = new JPDAStart(io, debugName, jdk);
+        String res = null;
+        try {
+            res = start.execute(p);
+        } catch (Throwable ex) {
+            Exceptions.printStackTrace(ex);
+        }
+        return Arrays.asList("--jvm.agentlib:jdwp=transport=dt_socket,address=" + res + ",server=n,suspend=y");
+    }
+
+    private static boolean isJavaPlatformBinDir(FileObject dir) {
+        if (!"bin".equals(dir.getNameExt())) {
+            return false;
+        }
+        FileObject file = dir.getFileObject("java", BaseUtilities.isWindows() ? "exe" : null);
+        if (file == null) {
+            return false;
+        }
+        file = dir.getFileObject("node", BaseUtilities.isWindows() ? "exe" : null);
+        return file != null;
+    }
+
+}
diff --git a/java/debugger.jpda.trufflenode/src/org/netbeans/modules/debugger/jpda/truffle/node/JPDAStart.java b/java/debugger.jpda.trufflenode/src/org/netbeans/modules/debugger/jpda/truffle/node/JPDAStart.java
new file mode 100644
index 0000000000..8a28534f9c
--- /dev/null
+++ b/java/debugger.jpda.trufflenode/src/org/netbeans/modules/debugger/jpda/truffle/node/JPDAStart.java
@@ -0,0 +1,267 @@
+/*
+ * 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.node;
+
+import com.sun.jdi.Bootstrap;
+import com.sun.jdi.connect.Connector;
+import com.sun.jdi.connect.ListeningConnector;
+import com.sun.jdi.connect.Transport;
+import java.beans.PropertyChangeEvent;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Logger;
+import java.util.logging.Level;
+import org.netbeans.api.debugger.DebuggerEngine;
+import org.netbeans.api.debugger.DebuggerManager;
+import org.netbeans.api.debugger.DebuggerManagerAdapter;
+import org.netbeans.api.debugger.jpda.DebuggerStartException;
+import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.api.debugger.jpda.MethodBreakpoint;
+import org.netbeans.api.java.platform.JavaPlatform;
+import org.netbeans.api.project.Project;
+import org.netbeans.modules.java.j2seplatform.api.J2SEPlatformCreator;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.util.RequestProcessor;
+import org.openide.windows.InputOutput;
+
+
+/**
+ * Start the JPDA debugger.
+ */
+public class JPDAStart implements Runnable {
+
+    private static final RequestProcessor RP = new RequestProcessor(JPDAStart.class);
+    
+    /**
+     * @parameter expression="${jpda.transport}"
+     */
+    private String transport = "dt_socket"; //NOI18N
+    
+    private String name;
+    
+    private final Object[] lock = new Object[2];
+    
+    private Project project;
+    private final String actionName;
+    private final InputOutput io;
+    private final FileObject javaHome;
+
+    JPDAStart(InputOutput inputOutput, String actionName, FileObject javaHome) {
+        io = inputOutput;
+        this.actionName = actionName;
+        this.javaHome = javaHome;
+    }
+    
+    /**
+     * returns the port/address that the debugger listens to..
+     */
+    public String execute(Project project) throws Throwable {
+        this.project = project;
+        io.getOut().println("JPDA Listening Start..."); //NOI18N
+//            getLog().debug("Entering synch lock"); //NOI18N
+        synchronized (lock) {
+//                getLog().debug("Entered synch lock"); //NOI18N
+            RP.post(this);
+//                    getLog().debug("Entering wait"); //NOI18N
+            lock.wait();
+//                    getLog().debug("Wait finished"); //NOI18N
+            if (lock[1] != null) {
+                throw ((Throwable) lock[1]); //NOI18N
+            }
+        }
+        return (String)lock[0];
+    }
+    
+    @Override
+    public void run() {
+        synchronized (lock) {
+            
+            try {
+                
+                ListeningConnector lc = null;
+                Iterator i = Bootstrap.virtualMachineManager().
+                        listeningConnectors().iterator();
+                for (; i.hasNext();) {
+                    lc = (ListeningConnector) i.next();
+                    Transport t = lc.transport();
+                    if (t != null && t.name().equals(getTransport())) {
+                        break;
+                    }
+                }
+                if (lc == null) {
+                    throw new RuntimeException
+                            ("No trasports named " + getTransport() + " found!"); //NOI18N
+                }
+                // TODO: revisit later when http://developer.java.sun.com/developer/bugParade/bugs/4932074.html gets integrated into JDK
+                // This code parses the address string "HOST:PORT" to extract PORT and then point debugee to localhost:PORT
+                // This is NOT a clean solution to the problem but it SHOULD work in 99% cases
+                final Map args = lc.defaultArguments();
+                String address = lc.startListening(args);
+                try {
+                    int port = Integer.parseInt(address.substring(address.indexOf(':') + 1));
+//                    getProject ().setNewProperty (getAddressProperty (), "localhost:" + port);
+                    Connector.IntegerArgument portArg = (Connector.IntegerArgument) args.get("port"); //NOI18N
+                    portArg.setValue(port);
+                    lock[0] = Integer.toString(port);
+                } catch (NumberFormatException e) {
+                    // this address format is not known, use default
+//                    getProject ().setNewProperty (getAddressProperty (), address);
+                    lock[0] = address;
+                }
+                io.getOut().println("JPDA Address: " + address); //NOI18N
+                io.getOut().println("Port:" + lock[0]); //NOI18N
+                
+                
+                final Map properties = new HashMap();
+//                properties.put("sourcepath", sourcePath); //NOI18N
+                properties.put("name", getName()); //NOI18N
+                JavaPlatform graalVM = J2SEPlatformCreator.createJ2SEPlatform(javaHome);
+                properties.put("jdksources", graalVM.getSourceFolders()); //NOI18N
+                properties.put("baseDir", FileUtil.toFile(project.getProjectDirectory())); // NOI18N
+                
+                final ListeningConnector flc = lc;
+                RP.post(new Runnable() {
+
+                    @Override
+                    public void run() {
+                        try {
+                            JPDADebugger.startListening(flc, args,
+                                                        new Object[]{properties, project});
+                        }
+                        catch (DebuggerStartException ex) {
+                            io.getErr().println("Debugger Start Error."); //NOI18N
+                            Logger.getLogger(JPDAStart.class.getName()).log(Level.INFO, "Debugger Start Error.", ex);
+                        }
+                    }
+                });
+            } catch (java.io.IOException ioex) {
+                io.getErr().println("IO Error:"); //NOI18N
+//                org.openide.ErrorManager.getDefault().notify(ioex);
+                lock[1] = ioex;
+            } catch (com.sun.jdi.connect.IllegalConnectorArgumentsException icaex) {
+                io.getErr().println("Illegal Connector"); //NOI18N
+                lock[1] = icaex;
+            } finally {
+                lock.notify();
+            }
+        }
+        
+    }
+    
+    private static class Listener extends DebuggerManagerAdapter {
+        
+        private MethodBreakpoint breakpoint;
+        private final Set debuggers = new HashSet();
+        
+        
+        Listener(MethodBreakpoint breakpoint) {
+            this.breakpoint = breakpoint;
+        }
+        
+        @Override
+        public void propertyChange(PropertyChangeEvent e) {
+            if (JPDADebugger.PROP_STATE.equals(e.getPropertyName())) {
+                int state = ((Integer) e.getNewValue()).intValue();
+                if ( (state == JPDADebugger.STATE_DISCONNECTED) ||
+                        (state == JPDADebugger.STATE_STOPPED)
+                        ) {
+                    if (breakpoint != null) {
+                        DebuggerManager.getDebuggerManager().
+                                removeBreakpoint(breakpoint);
+                        breakpoint = null;
+                    }
+                    dispose();
+                }
+            }
+        }
+        
+        private void dispose() {
+            DebuggerManager.getDebuggerManager().removeDebuggerListener(
+                    DebuggerManager.PROP_DEBUGGER_ENGINES,
+                    this
+                    );
+            Iterator it = debuggers.iterator();
+            while (it.hasNext()) {
+                JPDADebugger d = (JPDADebugger) it.next();
+                d.removePropertyChangeListener(
+                        JPDADebugger.PROP_STATE,
+                        this
+                        );
+            }
+        }
+        
+        @Override
+        public void engineAdded(DebuggerEngine engine) {
+            JPDADebugger debugger = engine.lookupFirst(null, JPDADebugger.class);
+            if (debugger == null) {
+                return;
+            }
+            debugger.addPropertyChangeListener(
+                    JPDADebugger.PROP_STATE,
+                    this
+                    );
+            debuggers.add(debugger);
+        }
+        
+        @Override
+        public void engineRemoved(DebuggerEngine engine) {
+            JPDADebugger debugger = engine.lookupFirst
+                    (null, JPDADebugger.class);
+            if (debugger == null) {
+                return;
+            }
+            debugger.removePropertyChangeListener(
+                    JPDADebugger.PROP_STATE,
+                    this
+                    );
+            debuggers.remove(debugger);
+        }
+
+        private void replaceBreakpoint(MethodBreakpoint b2) {
+            breakpoint = b2;
+        }
+        
+        
+    }
+    
+    public String getTransport() {
+        return transport;
+    }
+    
+    public void setTransport(String transport) {
+        this.transport = transport;
+    }
+    
+    public String getName() {
+        return name;
+    }
+    
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    
+    
+    
+    
+}
diff --git a/nbbuild/cluster.properties b/nbbuild/cluster.properties
index 57c6244324..99908712eb 100644
--- a/nbbuild/cluster.properties
+++ b/nbbuild/cluster.properties
@@ -572,6 +572,8 @@ nb.cluster.java=\
         debugger.jpda.kit,\
         debugger.jpda.projects,\
         debugger.jpda.projectsui,\
+        debugger.jpda.truffle,\
+        debugger.jpda.trufflenode,\
         debugger.jpda.ui,\
         debugger.jpda.visual,\
         editor.htmlui,\
@@ -1197,8 +1199,6 @@ nb.cluster.stableuc=\
         apisupport.apidocs,\
         cloud.oracle,\
         db.mysql.sakila,\
-        debugger.jpda.truffle,\
-        debugger.jpda.trufflenode,\
         deployment.deviceanywhere,\
         findbugs,\
         j2me.cdc.kit,\
diff --git a/nbbuild/licenses/UPL b/nbbuild/licenses/UPL
new file mode 100644
index 0000000000..1a7d22ca83
--- /dev/null
+++ b/nbbuild/licenses/UPL
@@ -0,0 +1,18 @@
+Copyright (c) __YEARS__, __NAMES__
+
+The Universal Permissive License (UPL), Version 1.0
+
+Subject to the condition set forth below, permission is hereby granted to any person obtaining a copy of this software, associated documentation and/or data (collectively the "Software"), free of charge and under any and all copyright rights in the Software, and any and all patent rights owned or freely licensable by each licensor hereunder covering either (i) the unmodified Software as contributed to or provided by such licensor, or (ii) the Larger Works (as defined below), to deal in both
+
+(a) the Software, and
+
+(b) any piece of software and/or hardware listed in the lrgrwrks.txt file if one is included with the Software each a "Larger Work" to which the Software is contributed by such licensors),
+
+without restriction, including without limitation the rights to copy, create derivative works of, display, perform, and distribute the Software and make, use, sell, offer for sale, import, export, have made, and have sold the Software and the Larger Work(s), and to sublicense the foregoing rights on either these or other terms.
+
+This license is subject to the following condition:
+
+The above copyright notice and either this complete permission notice or at a minimum a reference to the UPL must be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
diff --git a/platform/openide.loaders/apichanges.xml b/platform/openide.loaders/apichanges.xml
index 63c260cf59..a125085444 100644
--- a/platform/openide.loaders/apichanges.xml
+++ b/platform/openide.loaders/apichanges.xml
@@ -85,6 +85,22 @@ is the proper place.
 <!-- ACTUAL CHANGES BEGIN HERE: -->
 
   <changes>
+      <change id="org.openide.awt.Toolbar.toggle">
+          <api name="awt"/>
+          <summary>Separate template handling</summary>
+          <version major="7" minor="70"/>
+          <date day="27" month="9" year="2018"/>
+          <author login="mentlicher"/>
+          <compatibility addition="yes" deletion="no" binary="compatible" 
+                         source="compatible" deprecation="no"/>
+          <description>
+              Now it's possible to register actions with
+              <a href="@org-openide-awt@/org/openide/awt/Actions.html#ACTION_VALUE_TOGGLE">
+              toggle property</a> into Toolbar to get a toggle button.
+          </description>
+          <class package="org.openide.awt" name="Toolbar"/>
+          <issue number=""/>
+      </change>
       <change id="org.openide.loaders.DataFolder.SortMode.NATURAL">
           <api name="loaders"/>
           <summary>Introduces SortMode for natural sorting.</summary>
diff --git a/platform/openide.loaders/manifest.mf b/platform/openide.loaders/manifest.mf
index 52495733a0..f17ef90f5d 100644
--- a/platform/openide.loaders/manifest.mf
+++ b/platform/openide.loaders/manifest.mf
@@ -1,6 +1,6 @@
 Manifest-Version: 1.0
 OpenIDE-Module: org.openide.loaders
-OpenIDE-Module-Specification-Version: 7.69
+OpenIDE-Module-Specification-Version: 7.70
 OpenIDE-Module-Localizing-Bundle: org/openide/loaders/Bundle.properties
 OpenIDE-Module-Provides: org.netbeans.modules.templates.v1_0
 OpenIDE-Module-Layer: org/netbeans/modules/openide/loaders/layer.xml
diff --git a/platform/openide.loaders/src/org/openide/awt/Toolbar.java b/platform/openide.loaders/src/org/openide/awt/Toolbar.java
index 14226ca882..e9fb162126 100644
--- a/platform/openide.loaders/src/org/openide/awt/Toolbar.java
+++ b/platform/openide.loaders/src/org/openide/awt/Toolbar.java
@@ -38,6 +38,7 @@
 import javax.swing.JButton;
 import javax.swing.JComponent;
 import javax.swing.JSeparator;
+import javax.swing.JToggleButton;
 import javax.swing.JToolBar;
 import javax.swing.UIManager;
 import javax.swing.plaf.metal.MetalLookAndFeel;
@@ -436,7 +437,12 @@ protected Object createInstance(final InstanceCookie[] cookies)
                     }
                     if (obj instanceof Action) {
                         Action a = (Action) obj;
-                        JButton b = new DefaultIconButton();
+                        AbstractButton b;
+                        if (a.getValue(Actions.ACTION_VALUE_TOGGLE) != null) {
+                            b = new DefaultIconToggleButton();
+                        } else {
+                            b = new DefaultIconButton();
+                        }
 
                         if (ToolbarPool.getDefault().getPreferredIconSize() == 24) {
                             b.putClientProperty("PreferredIconSize", new Integer(24));
@@ -617,4 +623,23 @@ public Icon getIcon() {
         }
     }
 
+    /**
+     * A button that provides a default icon when no text and no custom icon have been set.
+     */
+    private static class DefaultIconToggleButton extends JToggleButton {
+        private Icon unknownIcon;
+
+        @Override
+        public Icon getIcon() {
+            Icon retValue = super.getIcon();
+            if( null == retValue && (null == getText() || getText().length() == 0 ) ) {
+                if (unknownIcon == null) {
+                    unknownIcon = ImageUtilities.loadImageIcon("org/openide/loaders/unknown.gif", false); //NOI18N
+                }
+                retValue = unknownIcon;
+            }
+            return retValue;
+        }
+    }
+
 } // end of class Toolbar


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services

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

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