You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by en...@apache.org on 2021/04/01 21:35:16 UTC

[netbeans] branch master updated (abd50d8 -> f0b395c)

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

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


    from abd50d8  LSP: Readme, changelog, and version modified for 12.4 Beta. (#2852)
     new e4a9fb4  Report closed InputOutputProvider. Not doing so cause problems on repeated write.
     new 0344bcf  Native debugging of GraalVM's native images.
     new f0b395c  The debug test needs to specify configuration, after a second Native Image configuration was added.

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 cpplite/cpplite.debugger/nbproject/project.xml     |  18 +
 .../modules/cpplite/debugger/CPPFrame.java         | 247 +++++++--
 .../modules/cpplite/debugger/CPPLiteDebugger.java  | 150 ++++--
 .../cpplite/debugger/CPPLiteDebuggerConfig.java    |  10 +-
 .../modules/cpplite/debugger/CPPThread.java        |  11 +-
 .../modules/cpplite/debugger/CPPVariable.java      |  61 ++-
 .../debugger/DebuggerBreakpointAnnotation.java     |  14 +-
 .../modules/cpplite/debugger/ThreadsCollector.java |   2 +-
 .../cpplite/debugger/ToolTipAnnotation.java        |  11 +-
 .../modules/cpplite/debugger/api/Debugger.java     |   6 +-
 .../breakpoints/BreakpointAnnotationProvider.java  |  13 +-
 .../debugger/breakpoints/BreakpointModel.java      |  15 +-
 .../debugger/breakpoints/BreakpointsReader.java    |  89 ++-
 .../debugger/breakpoints/CPPLiteBreakpoint.java    | 135 ++++-
 .../CPPLiteBreakpointActionProvider.java           |  33 +-
 .../debugger/breakpoints/PersistenceManager.java   |  13 +-
 .../debugger/debuggingview/DebuggingModel.java     |   4 +-
 .../cpplite/debugger/models/CallStackModel.java    |  27 +-
 .../cpplite/debugger/models/VariablesModel.java    |  52 +-
 .../cpplite/debugger/models/WatchesModel.java      |  27 +-
 .../modules/cpplite/debugger/ni/NIBreakpoints.java |  85 +++
 .../debugger/ni/NIDebuggerProviderImpl.java        | 172 ++++++
 .../NIDebuggerServiceProviderImpl.java}            |  25 +-
 .../cpplite/debugger/AbstractDebugTest.java        |   3 +-
 .../modules/cpplite/debugger/BreakpointsTest.java  |   8 +-
 .../modules/cpplite/debugger/StepTest.java         |   2 +-
 .../cpplite/project/ActionProviderImpl.java        |   2 +-
 ide/ide.kit/nbproject/project.xml                  |   7 +
 ide/nativeimage.api/build.xml                      |  28 +
 ide/nativeimage.api/manifest.mf                    |   6 +
 .../nativeimage.api}/nbproject/project.properties  |   0
 .../nativeimage.api}/nbproject/project.xml         | 100 +---
 .../modules/nativeimage/api/Bundle.properties      |  20 +
 .../nativeimage/api/debug}/EvaluateException.java  |  24 +-
 .../modules/nativeimage/api/debug/NIDebugger.java  | 211 ++++++++
 .../modules/nativeimage/api/debug/NIFrame.java     |  69 +++
 .../api/debug/NILineBreakpointDescriptor.java      | 179 +++++++
 .../modules/nativeimage/api/debug/NIVariable.java  |  96 ++++
 .../nativeimage/spi/debug/NIDebuggerProvider.java  | 123 +++++
 .../spi/debug/NIDebuggerServiceProvider.java       |  18 +-
 .../spi/debug/filters/FrameDisplayer.java          | 166 ++++++
 .../spi/debug/filters/VariableDisplayer.java       |  23 +-
 .../nativeimage/debug/NIDebuggerServiceTest.java   |  67 +++
 .../nativeimage/debug/TestNIDebuggerProvider.java  | 103 ++++
 .../debug/TestNIDebuggerServiceProvider.java       |  25 +-
 java/java.kit/nbproject/project.xml                |   7 +
 .../nbcode/nbproject/platform.properties           |   8 +-
 java/java.lsp.server/nbproject/project.xml         |  18 +
 java/java.lsp.server/script/etc/nbcode.clusters    |   1 +
 .../lsp/server/debugging/NbProtocolServer.java     | 115 ++--
 .../java/lsp/server/debugging/NbThreads.java       |  21 +-
 .../server/debugging/launch/NbDebugSession.java    |  27 +-
 .../server/debugging/launch/NbLaunchDelegate.java  | 219 +++++---
 .../debugging/launch/NbLaunchRequestHandler.java   |  64 ++-
 .../variables/NbVariablesRequestHandler.java       |  23 +-
 .../server/ui/AbstractLspInputOutputProvider.java  |   4 +-
 java/java.lsp.server/vscode/package.json           |  55 ++
 java/java.lsp.server/vscode/src/extension.ts       |  26 +
 .../vscode/src/test/suite/extension.test.ts        |   5 +-
 java/java.nativeimage.debugger/build.xml           |  28 +
 java/java.nativeimage.debugger/manifest.mf         |   7 +
 .../nbproject/project.properties                   |   0
 .../nbproject/project.xml                          |  88 +--
 .../java/nativeimage/debugger/Bundle.properties    |  20 +
 .../nativeimage/debugger/actions/Bundle.properties |  21 +
 .../debugger/actions/NIAttachCustomizer.form       | 125 +++++
 .../debugger/actions/NIAttachCustomizer.java       | 378 +++++++++++++
 .../nativeimage/debugger/actions/NIAttachType.java |  56 ++
 .../nativeimage/debugger/api/NIDebugRunner.java    |  90 ++++
 .../breakpoints/JPDABreakpointsHandler.java        | 183 +++++++
 .../debugger/displayer/JavaFrameDisplayer.java     | 206 +++++++
 .../debugger/displayer/JavaVariablesDisplayer.java | 596 +++++++++++++++++++++
 .../nativeimage/debugger/resources/mf-layer.xml    |  32 ++
 .../nativeimage/debugger/NIDebugRunnerTest.java    |  78 +++
 .../debugger/TestNIDebuggerProvider.java           | 122 +++++
 .../debugger/TestNIDebuggerServiceProvider.java    |  18 +-
 .../java/nativeimage/debugger/TestNIVariable.java  |  71 +--
 nbbuild/build.properties                           |   1 +
 nbbuild/cluster.properties                         |   2 +
 nbbuild/javadoctools/links.xml                     |   1 +
 nbbuild/javadoctools/properties.xml                |   1 +
 nbbuild/javadoctools/replaces.xml                  |   1 +
 82 files changed, 4592 insertions(+), 626 deletions(-)
 create mode 100644 cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/ni/NIBreakpoints.java
 create mode 100644 cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/ni/NIDebuggerProviderImpl.java
 copy cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/{EvaluateException.java => ni/NIDebuggerServiceProviderImpl.java} (61%)
 create mode 100644 ide/nativeimage.api/build.xml
 create mode 100644 ide/nativeimage.api/manifest.mf
 copy {webcommon/web.javascript.debugger => ide/nativeimage.api}/nbproject/project.properties (100%)
 copy {cpplite/cpplite.debugger => ide/nativeimage.api}/nbproject/project.xml (55%)
 create mode 100644 ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/Bundle.properties
 copy {cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger => ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug}/EvaluateException.java (66%)
 create mode 100644 ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIDebugger.java
 create mode 100644 ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIFrame.java
 create mode 100644 ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NILineBreakpointDescriptor.java
 create mode 100644 ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIVariable.java
 create mode 100644 ide/nativeimage.api/src/org/netbeans/modules/nativeimage/spi/debug/NIDebuggerProvider.java
 copy cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/EvaluateException.java => ide/nativeimage.api/src/org/netbeans/modules/nativeimage/spi/debug/NIDebuggerServiceProvider.java (72%)
 create mode 100644 ide/nativeimage.api/src/org/netbeans/modules/nativeimage/spi/debug/filters/FrameDisplayer.java
 copy cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/EvaluateException.java => ide/nativeimage.api/src/org/netbeans/modules/nativeimage/spi/debug/filters/VariableDisplayer.java (64%)
 create mode 100644 ide/nativeimage.api/test/unit/src/org/netbeans/modules/nativeimage/debug/NIDebuggerServiceTest.java
 create mode 100644 ide/nativeimage.api/test/unit/src/org/netbeans/modules/nativeimage/debug/TestNIDebuggerProvider.java
 copy cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/EvaluateException.java => ide/nativeimage.api/test/unit/src/org/netbeans/modules/nativeimage/debug/TestNIDebuggerServiceProvider.java (59%)
 create mode 100644 java/java.nativeimage.debugger/build.xml
 create mode 100644 java/java.nativeimage.debugger/manifest.mf
 copy {webcommon/web.javascript.debugger => java/java.nativeimage.debugger}/nbproject/project.properties (100%)
 copy {cpplite/cpplite.debugger => java/java.nativeimage.debugger}/nbproject/project.xml (78%)
 create mode 100644 java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/Bundle.properties
 create mode 100644 java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/actions/Bundle.properties
 create mode 100644 java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/actions/NIAttachCustomizer.form
 create mode 100644 java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/actions/NIAttachCustomizer.java
 create mode 100644 java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/actions/NIAttachType.java
 create mode 100644 java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/api/NIDebugRunner.java
 create mode 100644 java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/breakpoints/JPDABreakpointsHandler.java
 create mode 100644 java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/displayer/JavaFrameDisplayer.java
 create mode 100644 java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/displayer/JavaVariablesDisplayer.java
 create mode 100644 java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/resources/mf-layer.xml
 create mode 100644 java/java.nativeimage.debugger/test/unit/src/org/netbeans/modules/java/nativeimage/debugger/NIDebugRunnerTest.java
 create mode 100644 java/java.nativeimage.debugger/test/unit/src/org/netbeans/modules/java/nativeimage/debugger/TestNIDebuggerProvider.java
 rename cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/EvaluateException.java => java/java.nativeimage.debugger/test/unit/src/org/netbeans/modules/java/nativeimage/debugger/TestNIDebuggerServiceProvider.java (67%)
 copy cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPVariable.java => java/java.nativeimage.debugger/test/unit/src/org/netbeans/modules/java/nativeimage/debugger/TestNIVariable.java (50%)

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

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


[netbeans] 03/03: The debug test needs to specify configuration, after a second Native Image configuration was added.

Posted by en...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit f0b395c63b7d40ff18c8655bd2abe596aa04a3a0
Author: Martin Entlicher <ma...@oracle.com>
AuthorDate: Thu Apr 1 19:54:21 2021 +0200

    The debug test needs to specify configuration, after a second Native Image configuration was added.
---
 java/java.lsp.server/vscode/src/test/suite/extension.test.ts | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/java/java.lsp.server/vscode/src/test/suite/extension.test.ts b/java/java.lsp.server/vscode/src/test/suite/extension.test.ts
index 2d8053a..5f4e7ec 100644
--- a/java/java.lsp.server/vscode/src/test/suite/extension.test.ts
+++ b/java/java.lsp.server/vscode/src/test/suite/extension.test.ts
@@ -194,8 +194,9 @@ class Main {
                         then(() => waitUserApplication(5, false, () => resolve(true)));
                 }
                 console.log("Test: invoking debug debug.run");
-                vscode.commands.executeCommand("workbench.action.debug.run").then(
-                    () => waitUserApplication(5, true, onProcessStarted));
+                const workspaceFolder = (vscode.workspace.workspaceFolders!)[0];
+                vscode.debug.startDebugging(workspaceFolder, {type: "java8+", name: "Launch Java 8+ App", request: "launch"}, {}).
+                    then(() => waitUserApplication(5, true, onProcessStarted));
             });
             return r;
         } catch (error) {

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

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


[netbeans] 02/03: Native debugging of GraalVM's native images.

Posted by en...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 0344bcf958a48aff2f941738a0f7711bb5d2f3f9
Author: Martin Entlicher <ma...@oracle.com>
AuthorDate: Mon Mar 22 16:44:49 2021 +0100

    Native debugging of GraalVM's native images.
---
 cpplite/cpplite.debugger/nbproject/project.xml     |  18 +
 .../modules/cpplite/debugger/CPPFrame.java         | 247 +++++++--
 .../modules/cpplite/debugger/CPPLiteDebugger.java  | 150 ++++--
 .../cpplite/debugger/CPPLiteDebuggerConfig.java    |  10 +-
 .../modules/cpplite/debugger/CPPThread.java        |  11 +-
 .../modules/cpplite/debugger/CPPVariable.java      |  61 ++-
 .../debugger/DebuggerBreakpointAnnotation.java     |  14 +-
 .../modules/cpplite/debugger/ThreadsCollector.java |   2 +-
 .../cpplite/debugger/ToolTipAnnotation.java        |  11 +-
 .../modules/cpplite/debugger/api/Debugger.java     |   6 +-
 .../breakpoints/BreakpointAnnotationProvider.java  |  13 +-
 .../debugger/breakpoints/BreakpointModel.java      |  15 +-
 .../debugger/breakpoints/BreakpointsReader.java    |  89 ++-
 .../debugger/breakpoints/CPPLiteBreakpoint.java    | 135 ++++-
 .../CPPLiteBreakpointActionProvider.java           |  33 +-
 .../debugger/breakpoints/PersistenceManager.java   |  13 +-
 .../debugger/debuggingview/DebuggingModel.java     |   4 +-
 .../cpplite/debugger/models/CallStackModel.java    |  27 +-
 .../cpplite/debugger/models/VariablesModel.java    |  52 +-
 .../cpplite/debugger/models/WatchesModel.java      |  27 +-
 .../modules/cpplite/debugger/ni/NIBreakpoints.java |  85 +++
 .../debugger/ni/NIDebuggerProviderImpl.java        | 172 ++++++
 .../NIDebuggerServiceProviderImpl.java}            |  25 +-
 .../cpplite/debugger/AbstractDebugTest.java        |   3 +-
 .../modules/cpplite/debugger/BreakpointsTest.java  |   8 +-
 .../modules/cpplite/debugger/StepTest.java         |   2 +-
 .../cpplite/project/ActionProviderImpl.java        |   2 +-
 ide/ide.kit/nbproject/project.xml                  |   7 +
 ide/nativeimage.api/build.xml                      |  28 +
 ide/nativeimage.api/manifest.mf                    |   6 +
 ide/nativeimage.api/nbproject/project.properties   |  19 +
 .../nativeimage.api}/nbproject/project.xml         | 100 +---
 .../modules/nativeimage/api/Bundle.properties      |  20 +
 .../nativeimage/api/debug}/EvaluateException.java  |  24 +-
 .../modules/nativeimage/api/debug/NIDebugger.java  | 211 ++++++++
 .../modules/nativeimage/api/debug/NIFrame.java     |  69 +++
 .../api/debug/NILineBreakpointDescriptor.java      | 179 +++++++
 .../modules/nativeimage/api/debug/NIVariable.java  |  96 ++++
 .../nativeimage/spi/debug/NIDebuggerProvider.java  | 123 +++++
 .../spi/debug/NIDebuggerServiceProvider.java       |  18 +-
 .../spi/debug/filters/FrameDisplayer.java          | 166 ++++++
 .../spi/debug/filters/VariableDisplayer.java       |  23 +-
 .../nativeimage/debug/NIDebuggerServiceTest.java   |  67 +++
 .../nativeimage/debug/TestNIDebuggerProvider.java  | 103 ++++
 .../debug/TestNIDebuggerServiceProvider.java       |  25 +-
 java/java.kit/nbproject/project.xml                |   7 +
 .../nbcode/nbproject/platform.properties           |   8 +-
 java/java.lsp.server/nbproject/project.xml         |  18 +
 java/java.lsp.server/script/etc/nbcode.clusters    |   1 +
 .../lsp/server/debugging/NbProtocolServer.java     | 115 ++--
 .../java/lsp/server/debugging/NbThreads.java       |  21 +-
 .../server/debugging/launch/NbDebugSession.java    |  27 +-
 .../server/debugging/launch/NbLaunchDelegate.java  | 219 +++++---
 .../debugging/launch/NbLaunchRequestHandler.java   |  64 ++-
 .../variables/NbVariablesRequestHandler.java       |  23 +-
 java/java.lsp.server/vscode/package.json           |  55 ++
 java/java.lsp.server/vscode/src/extension.ts       |  26 +
 java/java.nativeimage.debugger/build.xml           |  28 +
 java/java.nativeimage.debugger/manifest.mf         |   7 +
 .../nbproject/project.properties                   |  19 +
 .../nbproject/project.xml                          |  88 +--
 .../java/nativeimage/debugger/Bundle.properties    |  20 +
 .../nativeimage/debugger/actions/Bundle.properties |  21 +
 .../debugger/actions/NIAttachCustomizer.form       | 125 +++++
 .../debugger/actions/NIAttachCustomizer.java       | 378 +++++++++++++
 .../nativeimage/debugger/actions/NIAttachType.java |  56 ++
 .../nativeimage/debugger/api/NIDebugRunner.java    |  90 ++++
 .../breakpoints/JPDABreakpointsHandler.java        | 183 +++++++
 .../debugger/displayer/JavaFrameDisplayer.java     | 206 +++++++
 .../debugger/displayer/JavaVariablesDisplayer.java | 596 +++++++++++++++++++++
 .../nativeimage/debugger/resources/mf-layer.xml    |  32 ++
 .../nativeimage/debugger/NIDebugRunnerTest.java    |  78 +++
 .../debugger/TestNIDebuggerProvider.java           | 122 +++++
 .../debugger/TestNIDebuggerServiceProvider.java    |  18 +-
 .../java/nativeimage/debugger/TestNIVariable.java  |  71 +--
 nbbuild/build.properties                           |   1 +
 nbbuild/cluster.properties                         |   2 +
 nbbuild/javadoctools/links.xml                     |   1 +
 nbbuild/javadoctools/properties.xml                |   1 +
 nbbuild/javadoctools/replaces.xml                  |   1 +
 80 files changed, 4624 insertions(+), 623 deletions(-)

diff --git a/cpplite/cpplite.debugger/nbproject/project.xml b/cpplite/cpplite.debugger/nbproject/project.xml
index d360c06..48ff741 100644
--- a/cpplite/cpplite.debugger/nbproject/project.xml
+++ b/cpplite/cpplite.debugger/nbproject/project.xml
@@ -52,6 +52,24 @@
                     </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.59</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.nativeimage.api</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>0</release-version>
+                        <specification-version>0.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
                     <code-name-base>org.netbeans.modules.projectapi</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPFrame.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPFrame.java
index 3e5fd4c..c7315f5 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPFrame.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPFrame.java
@@ -19,14 +19,16 @@
 package org.netbeans.modules.cpplite.debugger;
 
 import java.io.File;
+import java.net.MalformedURLException;
 import java.net.URI;
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Objects;
-import java.util.function.Consumer;
+import java.util.concurrent.CompletableFuture;
 import java.util.logging.Level;
 import java.util.logging.Logger;
+
 import org.netbeans.api.annotations.common.CheckForNull;
 import org.netbeans.modules.cnd.debugger.gdb2.mi.MIConst;
 import org.netbeans.modules.cnd.debugger.gdb2.mi.MIRecord;
@@ -34,10 +36,17 @@ import org.netbeans.modules.cnd.debugger.gdb2.mi.MIResult;
 import org.netbeans.modules.cnd.debugger.gdb2.mi.MITList;
 import org.netbeans.modules.cnd.debugger.gdb2.mi.MITListItem;
 import org.netbeans.modules.cnd.debugger.gdb2.mi.MIValue;
+import org.netbeans.modules.nativeimage.api.debug.EvaluateException;
+import org.netbeans.modules.nativeimage.api.debug.NIFrame;
+import org.netbeans.modules.nativeimage.api.debug.NIVariable;
+import org.netbeans.modules.nativeimage.spi.debug.filters.FrameDisplayer;
+import org.netbeans.modules.nativeimage.spi.debug.filters.FrameDisplayer.DisplayedFrame;
 import org.netbeans.spi.debugger.ui.DebuggingView.DVFrame;
+
 import org.openide.cookies.LineCookie;
 import org.openide.filesystems.FileObject;
 import org.openide.filesystems.FileUtil;
+import org.openide.filesystems.URLMapper;
 import org.openide.text.Line;
 import org.openide.util.Pair;
 
@@ -46,47 +55,62 @@ public final class CPPFrame implements DVFrame {
     private static final Logger LOGGER = Logger.getLogger(CPPFrame.class.getName());
 
     private final CPPThread thread;
-    private final String address;
-    public final String shortFileName;
-    public final String fullFileName;
-    public final String functionName;
-    public final int line;
+    private final DisplayedFrame displayedFrame;
+    private final NIFrame niFrame;
     public final int level;
 
-    private volatile Map<String, CPPVariable> variables;
+    private volatile Map<String, NIVariable> variables;
 
-    CPPFrame(CPPThread thread, MITList frame) {
+    private CPPFrame(CPPThread thread, DisplayedFrame displayedFrame, NIFrame niFrame) {
         Objects.requireNonNull(thread);
+        Objects.requireNonNull(displayedFrame);
+        Objects.requireNonNull(niFrame);
         this.thread = thread;
-        this.address = frame.getConstValue("addr");
-        this.shortFileName = frame.valueOf("file") != null ? frame.valueOf("file").asConst().value() : null;
-        this.functionName = frame.valueOf("func").asConst().value();
-        this.fullFileName = frame.valueOf("fullname") != null ? frame.valueOf("fullname").asConst().value() : null;
-        this.line = frame.valueOf("line") != null ? Integer.parseInt(frame.valueOf("line").asConst().value()) : 1;
-        if (frame.valueOf("level") != null) {
-            this.level = Integer.parseInt(frame.valueOf("level").asConst().value());
-        } else {
-            this.level = 0;
+        this.displayedFrame = displayedFrame;
+        this.niFrame = niFrame;
+        this.level = niFrame.getLevel();
+    }
+
+    static CPPFrame create(CPPThread thread, MITList frame) {
+        NIFrame niFrame = new NIFrameImpl(thread.getId(), frame);
+        FrameDisplayer frameDisplayer = thread.getDebugger().getContextProvider().lookupFirst(null, FrameDisplayer.class);
+        DisplayedFrame displayedFrame = frameDisplayer != null ? frameDisplayer.displayed(niFrame) : createDisplayedFrame(niFrame);
+        if (displayedFrame == null) {
+            return null; // Not to be displayed
         }
+        return new CPPFrame(thread, displayedFrame, niFrame);
     }
 
-    String getAddress() {
-        return address;
+    NIFrame getFrame() {
+        return niFrame;
     }
 
     @Override
     public String getName() {
-        return functionName;
+        return displayedFrame.getDisplayName();
+    }
+
+    public String getDescription() {
+        return displayedFrame.getDescription();
     }
 
     @CheckForNull
     public Line location() {
-        FileObject file = FileUtil.toFileObject(FileUtil.normalizeFile(new File(fullFileName)));
+        URI sourceURI = displayedFrame.getSourceURI();
+        if (sourceURI == null) {
+            return null;
+        }
+        FileObject file;
+        try {
+            file = URLMapper.findFileObject(sourceURI.toURL());
+        } catch (MalformedURLException ex) {
+            return null;
+        }
         if (file == null) {
             return null;
         }
         LineCookie lc = file.getLookup().lookup(LineCookie.class);
-        return lc.getLineSet().getOriginal(line - 1);
+        return lc.getLineSet().getOriginal(displayedFrame.getLine() - 1);
     }
 
     @Override
@@ -101,17 +125,12 @@ public final class CPPFrame implements DVFrame {
 
     @Override
     public URI getSourceURI() {
-        FileObject file = FileUtil.toFileObject(FileUtil.normalizeFile(new File(fullFileName)));
-        if (file != null) {
-            return file.toURI();
-        } else {
-            return null;
-        }
+        return displayedFrame.getSourceURI();
     }
 
     @Override
     public int getLine() {
-        return line;
+        return displayedFrame.getLine();
     }
 
     @Override
@@ -119,8 +138,8 @@ public final class CPPFrame implements DVFrame {
         return -1;
     }
 
-    public Map<String, CPPVariable> getVariables() {
-        Map<String, CPPVariable> vars = variables;
+    public Map<String, NIVariable> getVariables() {
+        Map<String, NIVariable> vars = variables;
         if (vars == null) {
             synchronized (this) {
                 vars = variables;
@@ -132,12 +151,13 @@ public final class CPPFrame implements DVFrame {
         return vars;
     }
 
-    static Map<String, CPPVariable> retrieveVariables(CPPFrame frame, CPPVariable parentVar) {
+    static Map<String, NIVariable> retrieveVariables(CPPFrame frame, CPPVariable parentVar) {
         MIRecord record;
         try {
             if (parentVar == null) {
                 record = frame.thread.getDebugger().sendAndGet("-stack-list-variables --thread " + frame.thread.getId() + " --frame " + frame.level + " --no-frame-filters 2");
             } else {
+                // from to
                 record = frame.thread.getDebugger().sendAndGet("-var-list-children --thread " + frame.thread.getId() + " --frame " + frame.level + " --all-values " + parentVar.getUniqueName());
             }
         } catch (InterruptedException ex) {
@@ -150,7 +170,7 @@ public final class CPPFrame implements DVFrame {
         if (results.isEmpty()) {
             return Collections.emptyMap();
         }
-        Map<String, CPPVariable> map = new LinkedHashMap<>(results.size());
+        Map<String, NIVariable> map = new LinkedHashMap<>(results.size());
         MIValue children = results.valueOf("children");
         if (children != null) {
             for (MITListItem item : children.asList()) {
@@ -160,11 +180,13 @@ public final class CPPFrame implements DVFrame {
                 int numChildren = Integer.parseInt(child.getConstValue("numchild"));
                 String type = child.getConstValue("type");
                 MIValue value = child.valueOf("value");
-                map.put(name, new CPPVariable(frame, uniqueName, name, type, value, numChildren));
+                map.put(name, new CPPVariable(frame, parentVar, uniqueName, name, type, value, numChildren));
             }
         } else {
             MIValue resultValue = results.valueOf("variables");
-            if (resultValue.isConst()) {
+            if (resultValue == null) {
+                return Collections.emptyMap();
+            } else if (resultValue.isConst()) {
                 return Collections.singletonMap(((MIConst) resultValue).value(), null);
             }
             results = (MITList) resultValue;
@@ -176,9 +198,11 @@ public final class CPPFrame implements DVFrame {
                 MIValue value = varList.valueOf("value");
                 Pair<String, Integer> uniqueVar = createVariable(frame, name);
                 String uniqueName = uniqueVar.first();
-                int numChildren = uniqueVar.second();
-                LOGGER.log(Level.FINE, "  {0} = ({1}) {2} ; [{3}]", new Object[]{name, type, value, numChildren});
-                map.put(name, new CPPVariable(frame, uniqueName, name, type, value, numChildren));
+                if (uniqueName != null) {
+                    int numChildren = uniqueVar.second();
+                    LOGGER.log(Level.FINE, "  {0} = ({1}) {2} ; [{3}]", new Object[]{name, type, value, numChildren});
+                    map.put(name, new CPPVariable(frame, parentVar, uniqueName, name, type, value, numChildren));
+                }
             }
         }
         return map;
@@ -191,9 +215,12 @@ public final class CPPFrame implements DVFrame {
         try {
             record = frame.thread.getDebugger().sendAndGet("-var-create --thread " + frame.thread.getId() + " --frame " + frame.level + " - " + "*" + " " + variableName);
             if (!record.isError() && !record.isEmpty()) {
-                uniqueName = record.results().getConstValue("name");
-                String numchild = record.results().getConstValue("numchild");
-                numChildren = Integer.parseInt(numchild);
+                String name = record.results().getConstValue("name");
+                if (!name.isEmpty()) {
+                    uniqueName = name;
+                    String numchild = record.results().getConstValue("numchild");
+                    numChildren = Integer.parseInt(numchild);
+                }
             }
         } catch (InterruptedException ex) {
         }
@@ -216,13 +243,21 @@ public final class CPPFrame implements DVFrame {
         return numChildren;
     }
 
-    public void evaluateLazy(String expression, Consumer<CPPVariable> result, Consumer<EvaluateException> exception) {
-        CPPVariable value = getVariables().get(expression);
+    private static final String MI_ERROR = "MI parse error: ";
+
+    public CompletableFuture<NIVariable> evaluateAsync(String expression) {
+        return evaluateAsync(expression, null);
+    }
+
+    public CompletableFuture<NIVariable> evaluateAsync(String expression, String resultName) {
+        String resultVarName = (resultName != null) ? resultName : expression;
+        CompletableFuture<NIVariable> result = new CompletableFuture<>();
+        NIVariable value = getVariables().get(expression);
         if (value != null) {
-            result.accept(value);
-            return;
+            result.complete(value);
+            return result;
         }
-        thread.getDebugger().send(new Command("-var-create - * " + expression) {
+        thread.getDebugger().send(new Command("-var-create --thread " + thread.getId() + " --frame " + level + " - * " + expression) {
             @Override
             protected void onDone(MIRecord record) {
                 MITList results = record.results();
@@ -237,14 +272,128 @@ public final class CPPFrame implements DVFrame {
                 } else {
                     numChildren = retrieveNumChildren(CPPFrame.this, varName);
                 }
-                result.accept(new CPPVariable(CPPFrame.this, expression, expression, type, resultValue, numChildren));
-                thread.getDebugger().send(new Command("-var-delete " + varName));
+                result.complete(new CPPVariable(CPPFrame.this, null, varName, resultVarName, type, resultValue, numChildren));
+                //thread.getDebugger().send(new Command("-var-delete " + varName));
             }
             @Override
             protected void onError(MIRecord record) {
-                exception.accept(new EvaluateException(record.error()));
+                String error = record.error();
+                if (error.startsWith(MI_ERROR)) {
+                    error = error.substring(MI_ERROR.length());
+                    result.completeExceptionally(new EvaluateException(error));
+                }
             }
         });
+        return result;
+    }
+
+    private static DisplayedFrame createDisplayedFrame(NIFrame frame) {
+        return DisplayedFrame.newBuilder(getDisplayName(frame))
+                .description(getDescription(frame))
+                .line(frame.getLine())
+                .sourceURISupplier(() -> getSourceURI(frame))
+                .build();
+    }
+
+    private static String getDisplayName(NIFrame frame) {
+        StringBuilder builder = new StringBuilder(frame.getFunctionName());
+        String shortName = frame.getShortFileName();
+        if (shortName != null) {
+            builder.append("; ");
+            builder.append(shortName);
+        }
+        int line = frame.getLine();
+        if (line > 0) {
+            builder.append(':');
+            builder.append(line);
+        }
+        return builder.toString();
+    }
+
+    private static String getDescription(NIFrame frame) {
+        StringBuilder builder = new StringBuilder(frame.getFunctionName());
+        String fullName = frame.getFullFileName();
+        if (fullName != null) {
+            builder.append("; ");
+            builder.append(fullName);
+        }
+        int line = frame.getLine();
+        if (line > 0) {
+            builder.append(':');
+            builder.append(line);
+        }
+        return builder.toString();
+    }
 
+    private static URI getSourceURI(NIFrame frame) {
+        String fullFileName = frame.getFullFileName();
+        if (fullFileName != null && !fullFileName.isEmpty()) {
+            FileObject file = FileUtil.toFileObject(FileUtil.normalizeFile(new File(fullFileName)));
+            if (file != null) {
+                return file.toURI();
+            }
+        }
+        return null;
     }
+
+    private static final class NIFrameImpl implements NIFrame {
+
+        private final String threadId;
+        private final int level;
+        private final String address;
+        private final String shortFileName;
+        private final String fullFileName;
+        private final String functionName;
+        private final int line;
+
+        NIFrameImpl(String threadId, MITList frame) {
+            this.threadId = threadId;
+            this.address = frame.getConstValue("addr");
+            this.shortFileName = frame.valueOf("file") != null ? frame.valueOf("file").asConst().value() : null;
+            this.functionName = frame.valueOf("func").asConst().value();
+            this.fullFileName = frame.valueOf("fullname") != null ? frame.valueOf("fullname").asConst().value() : null;
+            this.line = frame.valueOf("line") != null ? Integer.parseInt(frame.valueOf("line").asConst().value()) : -1;
+            if (frame.valueOf("level") != null) {
+                this.level = Integer.parseInt(frame.valueOf("level").asConst().value());
+            } else {
+                this.level = 0;
+            }
+        }
+
+        @Override
+        public String getAddress() {
+            return address;
+        }
+
+        @Override
+        public String getShortFileName() {
+            return shortFileName;
+        }
+
+        @Override
+        public String getFullFileName() {
+            return fullFileName;
+        }
+
+        @Override
+        public String getFunctionName() {
+            return functionName;
+        }
+
+        @Override
+        public int getLine() {
+            return line;
+        }
+
+        @Override
+        public String getThreadId() {
+            return threadId;
+        }
+
+        @Override
+        public int getLevel() {
+            return level;
+        }
+    }
+
 }
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPLiteDebugger.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPLiteDebugger.java
index 87d6587..6ca0f17 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPLiteDebugger.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPLiteDebugger.java
@@ -28,6 +28,7 @@ import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.EventListener;
 import java.util.List;
 import java.util.Map;
@@ -51,6 +52,7 @@ import org.netbeans.modules.cnd.debugger.gdb2.mi.MIConst;
 import org.netbeans.modules.cnd.debugger.gdb2.mi.MIProxy;
 import org.netbeans.modules.cnd.debugger.gdb2.mi.MIRecord;
 import org.netbeans.modules.cnd.debugger.gdb2.mi.MITList;
+import org.netbeans.modules.cnd.debugger.gdb2.mi.MITListItem;
 import org.netbeans.modules.cnd.debugger.gdb2.mi.MIValue;
 import org.netbeans.modules.cpplite.debugger.breakpoints.CPPLiteBreakpoint;
 import org.netbeans.modules.nativeexecution.api.ExecutionEnvironmentFactory;
@@ -125,6 +127,10 @@ public final class CPPLiteDebugger {
         proxy.send(new Command("-gdb-set target-async"));
         //proxy.send(new Command("-gdb-set scheduler-locking on"));
         proxy.send(new Command("-gdb-set non-stop on"));
+        proxy.send(new Command("-gdb-set print object on"));
+    }
+
+    public void execRun() {
         proxy.send(new Command("-exec-run"));
     }
 
@@ -365,6 +371,46 @@ public final class CPPLiteDebugger {
         LOGGER.fine("finish() done, build finished.");
     }
 
+    public String readMemory(String address, long offset, int length) {
+        MIRecord memory;
+        String offsetArg;
+        if (offset != 0) {
+            offsetArg = "-o " + offset + " ";
+        } else {
+            offsetArg = "";
+        }
+        try {
+            memory = sendAndGet("-data-read-memory-bytes " + offsetArg + address + " " + length);
+        } catch (InterruptedException ex) {
+            return null;
+        }
+        MIValue memoryValue = memory.results().valueOf("memory");
+        if (memoryValue instanceof MITList) {
+            MITList memoryList = (MITList) memoryValue;
+            if (!memoryList.isEmpty()) {
+                MITListItem row = memoryList.get(0);
+                if (row instanceof MITList) {
+                    String contents = ((MITList) row).getConstValue("contents");
+                    return contents;
+                }
+            }
+        }
+        return null;
+    }
+
+    public String getVersion() {
+        MIRecord versionRecord;
+        try {
+            versionRecord = sendAndGet("-gdb-version");
+        } catch (InterruptedException ex) {
+            return null;
+        }
+        return versionRecord.results().toString();
+    }
+
+    ContextProvider getContextProvider() {
+        return contextProvider;
+    }
 
     DebuggingView.DVSupport getDVSupport() {
         return contextProvider.lookupFirst(null, DebuggingView.DVSupport.class);
@@ -421,7 +467,7 @@ public final class CPPLiteDebugger {
                                 break;
                             default:
                                 MITList topFrameList = (MITList) results.valueOf("frame");
-                                CPPFrame frame = topFrameList != null ? new CPPFrame(thread, topFrameList) : null;
+                                CPPFrame frame = topFrameList != null ? CPPFrame.create(thread, topFrameList) : null;
                                 thread.setTopFrame(frame);
                                 setSuspended(true, thread, frame);
                                 if (frame != null) {
@@ -497,8 +543,16 @@ public final class CPPLiteDebugger {
         void send(MICommand cmd, boolean waitForRunning) {
             if (waitForRunning) {
                 waitRunning();
+                send(cmd);
+            } else {
+                try {
+                    startedLatch.await();
+                } catch (InterruptedException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+                LOGGER.log(Level.FINE, "MIProxy.send({0})", cmd);
+                super.send(cmd);
             }
-            send(cmd);
         }
 
         @Override
@@ -548,40 +602,41 @@ public final class CPPLiteDebugger {
 
     }
 
-    public static @NonNull Pair<DebuggerEngine, Process> startDebugging (CPPLiteDebuggerConfig configuration) throws IOException {
-        DebuggerInfo di = DebuggerInfo.create (
-            "CPPLiteDebuggerInfo",
-            new Object[] {
-                new SessionProvider () {
-                    @Override
-                    public String getSessionName () {
-                        return configuration.getDisplayName ();
-                    }
-
-                    @Override
-                    public String getLocationName () {
-                        return "localhost";
-                    }
+    public static @NonNull Pair<DebuggerEngine, Process> startDebugging (CPPLiteDebuggerConfig configuration, Object... services) throws IOException {
+        SessionProvider sessionProvider = new SessionProvider () {
+            @Override
+            public String getSessionName () {
+                return configuration.getDisplayName ();
+            }
 
-                    @Override
-                    public String getTypeID () {
-                        return "CPPLiteSession";
-                    }
+            @Override
+            public String getLocationName () {
+                return "localhost";
+            }
 
-                    @Override
-                    public Object[] getServices () {
-                        return new Object[] {};
-                    }
-                },
-                configuration
+            @Override
+            public String getTypeID () {
+                return "CPPLiteSession";
             }
+
+            @Override
+            public Object[] getServices () {
+                return new Object[] {};
+            }
+        };
+        Object[] allServices = Arrays.copyOf(services, services.length + 2);
+        allServices[services.length] = sessionProvider;
+        allServices[services.length + 1] = configuration;
+        DebuggerInfo di = DebuggerInfo.create(
+            "CPPLiteDebuggerInfo",
+            allServices
         );
         DebuggerEngine[] es = DebuggerManager.getDebuggerManager ().
             startDebugging (di);
         Pty pty = PtySupport.allocate(ExecutionEnvironmentFactory.getLocal());
         CPPLiteDebugger debugger = es[0].lookupFirst(null, CPPLiteDebugger.class);
         List<String> executable = new ArrayList<>();
-        executable.add("gdb");
+        executable.add(configuration.getDebugger());
         executable.add("--interpreter=mi");
         executable.add("--tty=" + pty.getSlaveName());
         executable.addAll(configuration.getExecutable());
@@ -700,30 +755,27 @@ public final class CPPLiteDebugger {
         }
 
         private void addBreakpoint(CPPLiteBreakpoint breakpoint) {
-            Line l = breakpoint.getLine();
-            FileObject source = l.getLookup().lookup(FileObject.class);
-            File sourceFile = source != null ? FileUtil.toFile(source) : null;
-            if (sourceFile != null) {
-                String disabled = breakpoint.isEnabled() ? "" : "-d ";
-                Command command = new Command("-break-insert " + disabled + sourceFile.getAbsolutePath() + ":" + (l.getLineNumber() + 1)) {
-                    @Override
-                    protected void onDone(MIRecord record) {
-                        MIValue bkpt = record.results().valueOf("bkpt");
-                        if (bkpt instanceof MITList) {
-                            breakpointResolved(breakpoint, (MITList) bkpt);
-                        }
-                        super.onDone(record);
+            String path = breakpoint.getFilePath();
+            int lineNumber = breakpoint.getLineNumber();
+            String disabled = breakpoint.isEnabled() ? "" : "-d ";
+            Command command = new Command("-break-insert " + disabled + path + ":" + lineNumber) {
+                @Override
+                protected void onDone(MIRecord record) {
+                    MIValue bkpt = record.results().valueOf("bkpt");
+                    if (bkpt instanceof MITList) {
+                        breakpointResolved(breakpoint, (MITList) bkpt);
                     }
+                    super.onDone(record);
+                }
 
-                    @Override
-                    protected void onError(MIRecord record) {
-                        String msg = record.results().getConstValue("msg");
-                        breakpointError(breakpoint, msg);
-                        super.onError(record);
-                    }
-                };
-                proxy.send(command);
-            }
+                @Override
+                protected void onError(MIRecord record) {
+                    String msg = record.results().getConstValue("msg");
+                    breakpointError(breakpoint, msg);
+                    super.onError(record);
+                }
+            };
+            proxy.send(command, false);
             breakpoint.addPropertyChangeListener(this);
         }
 
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPLiteDebuggerConfig.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPLiteDebuggerConfig.java
index f88b745..d604d12 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPLiteDebuggerConfig.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPLiteDebuggerConfig.java
@@ -26,14 +26,16 @@ import java.util.List;
  *
  * @author lahvac
  */
-public class CPPLiteDebuggerConfig {
+public final class CPPLiteDebuggerConfig {
 
     private final List<String> executable;
     private final File directory;
+    private final String debugger;
 
-    public CPPLiteDebuggerConfig(List<String> executable, File directory) {
+    public CPPLiteDebuggerConfig(List<String> executable, File directory, String debugger) {
         this.executable = executable;
         this.directory = directory;
+        this.debugger = debugger;
     }
 
     public String getDisplayName() {
@@ -47,4 +49,8 @@ public class CPPLiteDebuggerConfig {
     public File getDirectory() {
         return directory;
     }
+
+    public String getDebugger() {
+        return debugger;
+    }
 }
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPThread.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPThread.java
index 6158f6e..cfaa991 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPThread.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPThread.java
@@ -21,6 +21,7 @@ package org.netbeans.modules.cpplite.debugger;
 import java.beans.PropertyChangeListener;
 import java.beans.PropertyChangeSupport;
 import java.util.AbstractList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -230,8 +231,14 @@ public final class CPPThread implements DVThread {
                 stack[0] = topFrame;
                 i++;
             }
-            for (; i < l; i++) {
-                stack[i] = new CPPFrame(this, (MITList) ((MIResult) stackList.get(i)).value());
+            for (int li = i; li < l; li++) {
+                CPPFrame frame = CPPFrame.create(this, (MITList) ((MIResult) stackList.get(li)).value());
+                if (frame != null) {
+                    stack[i++] = frame;
+                }
+            }
+            if (i < l) {
+                stack = Arrays.copyOf(stack, i);
             }
             this.stack = stack;
         }
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPVariable.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPVariable.java
index cd5c6fe..ec1ef37 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPVariable.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPVariable.java
@@ -18,27 +18,33 @@
  */
 package org.netbeans.modules.cpplite.debugger;
 
+import java.util.Arrays;
 import java.util.Map;
 import java.util.Objects;
 
 import org.netbeans.modules.cnd.debugger.gdb2.mi.MIConst;
+import org.netbeans.modules.cnd.debugger.gdb2.mi.MIRecord;
 import org.netbeans.modules.cnd.debugger.gdb2.mi.MIValue;
+import org.netbeans.modules.nativeimage.api.debug.NIFrame;
+import org.netbeans.modules.nativeimage.api.debug.NIVariable;
 
 /**
  * Representation of a variable.
  */
-public final class CPPVariable {
+public final class CPPVariable implements NIVariable {
 
     private final CPPFrame frame;
+    private final CPPVariable parentVariable;
     private final String uniqueName;
     private final String name;
     private final String type;
     private final String value;
     private final int numChildren;
-    private volatile Map<String, CPPVariable> children;
+    private volatile Map<String, NIVariable> children;
 
-    CPPVariable(CPPFrame frame, String uniqueName, String name, String type, MIValue value, int numChildren) {
+    CPPVariable(CPPFrame frame, CPPVariable parentVariable, String uniqueName, String name, String type, MIValue value, int numChildren) {
         this.frame = frame;
+        this.parentVariable = parentVariable;
         this.uniqueName = uniqueName;
         this.name = name;
         this.type = type;
@@ -46,28 +52,42 @@ public final class CPPVariable {
         this.numChildren = numChildren;
     }
 
+    @Override
+    public NIFrame getFrame() {
+        return frame.getFrame();
+    }
+
+    @Override
+    public CPPVariable getParent() {
+        return parentVariable;
+    }
+
     public String getUniqueName() {
         return uniqueName;
     }
 
+    @Override
     public String getName() {
         return name;
     }
 
+    @Override
     public String getType() {
         return type;
     }
 
+    @Override
     public String getValue() {
         return value;
     }
 
+    @Override
     public int getNumChildren() {
         return numChildren;
     }
 
-    public Map<String, CPPVariable> getChildrenVariables() {
-        Map<String, CPPVariable> vars = children;
+    public Map<String, NIVariable> getChildrenByNames() {
+        Map<String, NIVariable> vars = children;
         if (vars == null) {
             synchronized (this) {
                 vars = children;
@@ -78,4 +98,35 @@ public final class CPPVariable {
         }
         return vars;
     }
+
+    @Override
+    public NIVariable[] getChildren(int from, int to) {
+        Map<String, NIVariable> childrenVariables = getChildrenByNames();
+        NIVariable[] array = childrenVariables.values().toArray(new NIVariable[0]);
+        if (array.length == 1 && array[0] == null) {
+            return new NIVariable[0]; // Error
+        }
+        if (from >= 0) {
+            to = Math.min(to, array.length);
+            if (from < to) {
+                array = Arrays.copyOfRange(array, from, to);
+            } else {
+                array = new NIVariable[0];
+            }
+        }
+        return array;
+    }
+
+    @Override
+    public String getExpressionPath() {
+        MIRecord pathRecord;
+        try {
+            pathRecord = frame.getThread().getDebugger().sendAndGet("-var-info-path-expression " + uniqueName);
+        } catch (InterruptedException ex) {
+            return null;
+        }
+        String pathExpression = pathRecord.results().getConstValue("path_expr");
+        return pathExpression;
+    }
+
 }
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/DebuggerBreakpointAnnotation.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/DebuggerBreakpointAnnotation.java
index f5e90b3..a169105 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/DebuggerBreakpointAnnotation.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/DebuggerBreakpointAnnotation.java
@@ -21,12 +21,14 @@ package org.netbeans.modules.cpplite.debugger;
 
 import java.util.LinkedList;
 import java.util.List;
+import org.netbeans.api.annotations.common.CheckForNull;
 
 import org.netbeans.api.debugger.Breakpoint;
 import org.netbeans.api.debugger.Breakpoint.HIT_COUNT_FILTERING_STYLE;
 import org.netbeans.modules.cpplite.debugger.breakpoints.CPPLiteBreakpoint;
 import org.netbeans.spi.debugger.ui.BreakpointAnnotation;
 import org.openide.text.Annotatable;
+import org.openide.text.Line;
 import org.openide.util.NbBundle;
 
 
@@ -49,13 +51,21 @@ public class DebuggerBreakpointAnnotation extends BreakpointAnnotation {
     private final String type;
     private final CPPLiteBreakpoint breakpoint;
 
-    public DebuggerBreakpointAnnotation (String type, CPPLiteBreakpoint b) {
+    private DebuggerBreakpointAnnotation (String type, Annotatable annotatable, CPPLiteBreakpoint b) {
         this.type = type;
         this.breakpoint = b;
-        Annotatable annotatable = b.getLine ();
         attach (annotatable);
     }
 
+    @CheckForNull
+    public static DebuggerBreakpointAnnotation create(String type, CPPLiteBreakpoint b) {
+        Line line = b.getLine();
+        if (line == null) {
+            return null;
+        }
+        return new DebuggerBreakpointAnnotation(type, line, b);
+    }
+
     @Override
     public String getAnnotationType () {
         return type;
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/ThreadsCollector.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/ThreadsCollector.java
index 4689901..d39efa8 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/ThreadsCollector.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/ThreadsCollector.java
@@ -68,7 +68,7 @@ public final class ThreadsCollector {
         }
     }
 
-    CPPThread get(String id) {
+    public CPPThread get(String id) {
         synchronized (threads) {
             return threads.get(id);
         }
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/ToolTipAnnotation.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/ToolTipAnnotation.java
index 88f571c..9fadadb 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/ToolTipAnnotation.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/ToolTipAnnotation.java
@@ -37,6 +37,7 @@ import org.openide.util.RequestProcessor;
 import org.netbeans.api.debugger.DebuggerManager;
 
 import org.netbeans.spi.debugger.ui.EditorContextDispatcher;
+import org.netbeans.modules.nativeimage.spi.debug.filters.VariableDisplayer;
 
 
 public class ToolTipAnnotation extends Annotation implements Runnable {
@@ -96,8 +97,11 @@ public class ToolTipAnnotation extends Annotation implements Runnable {
         if (d == null || (frame = d.getCurrentFrame()) == null) {
             return;
         }
-        frame.evaluateLazy(expression,
-                           variable -> {
+        frame.evaluateAsync(expression).thenAccept(variable -> {
+                               VariableDisplayer displayer = currentEngine.lookupFirst(null, VariableDisplayer.class);
+                               if (displayer != null) {
+                                   variable = displayer.displayed(variable)[0];
+                               }
                                String value = variable.getValue();
                                if (!value.equals(expression)) {
                                    String toolTipText;
@@ -109,9 +113,10 @@ public class ToolTipAnnotation extends Annotation implements Runnable {
                                    }
                                    firePropertyChange (PROP_SHORT_DESCRIPTION, null, toolTipText);
                                }
-                           }, exception -> {
+                           }).exceptionally(exception -> {
                                String toolTipText = exception.getLocalizedMessage();
                                firePropertyChange (PROP_SHORT_DESCRIPTION, null, toolTipText);
+                               return null;
                            });
     }
 
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/api/Debugger.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/api/Debugger.java
index f4f111f..03e5c62 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/api/Debugger.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/api/Debugger.java
@@ -21,8 +21,10 @@ package org.netbeans.modules.cpplite.debugger.api;
 import java.io.File;
 import java.io.IOException;
 import java.util.List;
+import org.netbeans.api.debugger.DebuggerEngine;
 import org.netbeans.modules.cpplite.debugger.CPPLiteDebugger;
 import org.netbeans.modules.cpplite.debugger.CPPLiteDebuggerConfig;
+import org.openide.util.Pair;
 
 /**
  *
@@ -36,6 +38,8 @@ public class Debugger {
     }
 
     public static Process startInDebugger(List<String> command, File directory) throws IOException {
-        return CPPLiteDebugger.startDebugging(new CPPLiteDebuggerConfig(command, directory)).second();
+        Pair<DebuggerEngine, Process> engineProcess = CPPLiteDebugger.startDebugging(new CPPLiteDebuggerConfig(command, directory, "gdb"));
+        engineProcess.first().lookupFirst(null, CPPLiteDebugger.class).execRun();
+        return engineProcess.second();
     }
 }
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/BreakpointAnnotationProvider.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/BreakpointAnnotationProvider.java
index aac9137..b55783f 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/BreakpointAnnotationProvider.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/BreakpointAnnotationProvider.java
@@ -104,7 +104,7 @@ public class BreakpointAnnotationProvider extends DebuggerManagerAdapter impleme
             for (Breakpoint breakpoint : DebuggerManager.getDebuggerManager().getBreakpoints()) {
                 if (breakpoint instanceof CPPLiteBreakpoint) {
                     CPPLiteBreakpoint b = (CPPLiteBreakpoint) breakpoint;
-                    if (isAt(b, fo)) {
+                    if (!b.isHidden() && isAt(b, fo)) {
                         if (!breakpointToAnnotations.containsKey(b)) {
                             b.addPropertyChangeListener(this);
                         }
@@ -118,13 +118,13 @@ public class BreakpointAnnotationProvider extends DebuggerManagerAdapter impleme
     }
 
     private static boolean isAt(CPPLiteBreakpoint b, FileObject fo) {
-        FileObject bfo = (FileObject) b.getLine().getLookup().lookup(FileObject.class);
+        FileObject bfo = b.getFileObject();
         return fo.equals(bfo);
     }
 
     @Override
     public void breakpointAdded(Breakpoint breakpoint) {
-        if (breakpoint instanceof CPPLiteBreakpoint) {
+        if (breakpoint instanceof CPPLiteBreakpoint && !((CPPLiteBreakpoint) breakpoint).isHidden()) {
             postAnnotationRefresh((CPPLiteBreakpoint) breakpoint, false, true);
             breakpoint.addPropertyChangeListener (this);
         }
@@ -132,7 +132,7 @@ public class BreakpointAnnotationProvider extends DebuggerManagerAdapter impleme
 
     @Override
     public void breakpointRemoved(Breakpoint breakpoint) {
-        if (breakpoint instanceof CPPLiteBreakpoint) {
+        if (breakpoint instanceof CPPLiteBreakpoint && !((CPPLiteBreakpoint) breakpoint).isHidden()) {
             breakpoint.removePropertyChangeListener (this);
             postAnnotationRefresh((CPPLiteBreakpoint) breakpoint, true, false);
         }
@@ -231,7 +231,10 @@ public class BreakpointAnnotationProvider extends DebuggerManagerAdapter impleme
         String condition = getCondition(b);
         boolean isConditional = condition.trim().length() > 0 || b.getHitCountFilteringStyle() != null;
         String annotationType = getAnnotationType(b, isConditional, breakpointsActive);
-        DebuggerBreakpointAnnotation annotation = new DebuggerBreakpointAnnotation (annotationType, b);
+        DebuggerBreakpointAnnotation annotation = DebuggerBreakpointAnnotation.create(annotationType, b);
+        if (annotation == null) {
+            return ;
+        }
         Set<Annotation> bpAnnotations = breakpointToAnnotations.get(b);
         if (bpAnnotations == null) {
             Set<Annotation> set = new WeakSet<>();
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/BreakpointModel.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/BreakpointModel.java
index 6939797..11d1757 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/BreakpointModel.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/BreakpointModel.java
@@ -19,6 +19,7 @@
 
 package org.netbeans.modules.cpplite.debugger.breakpoints;
 
+import java.io.File;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
 
@@ -65,9 +66,15 @@ public class BreakpointModel implements NodeModel {
     public String getDisplayName (Object node) throws UnknownTypeException {
         if (node instanceof CPPLiteBreakpoint) {
             CPPLiteBreakpoint breakpoint = (CPPLiteBreakpoint) node;
-            FileObject fileObject = breakpoint.getLine().getLookup().lookup(FileObject.class);
-            return fileObject.getNameExt () + ":" + 
-                (breakpoint.getLine ().getLineNumber () + 1);
+            String nameExt;
+            FileObject fileObject = breakpoint.getFileObject();
+            if (fileObject != null) {
+                nameExt = fileObject.getNameExt();
+            } else {
+                File file = new File(breakpoint.getFilePath());
+                nameExt = file.getName();
+            }
+            return nameExt + ":" + breakpoint.getLineNumber();
         }
         throw new UnknownTypeException (node);
     }
@@ -114,7 +121,7 @@ public class BreakpointModel implements NodeModel {
     throws UnknownTypeException {
         if (node instanceof CPPLiteBreakpoint) {
             CPPLiteBreakpoint breakpoint = (CPPLiteBreakpoint) node;
-            return breakpoint.getLine ().getDisplayName ();
+            return breakpoint.getFilePath() + ":" + breakpoint.getLineNumber();
         }
         throw new UnknownTypeException (node);
     }
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/BreakpointsReader.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/BreakpointsReader.java
index e0d9020..ae26933 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/BreakpointsReader.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/BreakpointsReader.java
@@ -21,42 +21,50 @@ package org.netbeans.modules.cpplite.debugger.breakpoints;
 
 import java.net.MalformedURLException;
 import java.net.URL;
+
 import org.netbeans.api.debugger.Breakpoint;
 import org.netbeans.api.debugger.Properties;
 import org.netbeans.spi.debugger.DebuggerServiceRegistration;
-import org.openide.cookies.LineCookie;
 import org.openide.filesystems.FileObject;
 import org.openide.filesystems.URLMapper;
-import org.openide.loaders.DataObject;
-import org.openide.loaders.DataObjectNotFoundException;
-import org.openide.text.Line;
-
 
-/**
- *
- * @author Jan Jancura
- */
 @DebuggerServiceRegistration(types={Properties.Reader.class})
 public class BreakpointsReader implements Properties.Reader {
-    
-    
+
     @Override
     public String [] getSupportedClassNames () {
         return new String[] {
-            CPPLiteBreakpoint.class.getName (), 
+            CPPLiteBreakpoint.class.getName (),
         };
     }
-    
+
     @Override
     public Object read (String typeID, Properties properties) {
         if (!(typeID.equals (CPPLiteBreakpoint.class.getName ())))
             return null;
-        
-        Line line = getLine (
-            properties.getString ("url", null),
-            properties.getInt ("lineNumber", 1));
-        if (line == null) return null;
-        CPPLiteBreakpoint b = new CPPLiteBreakpoint (line);
+
+        CPPLiteBreakpoint b;
+        int lineNumber = properties.getInt("lineNumber", 0) + 1;
+        String url = properties.getString ("url", null);
+        if (url != null) {
+            FileObject fo;
+            try {
+                fo = URLMapper.findFileObject(new URL(url));
+            } catch (MalformedURLException ex) {
+                fo = null;
+            }
+            if (fo == null) {
+                // The user file is gone
+                return null;
+            }
+            b = CPPLiteBreakpoint.create(fo, lineNumber);
+        } else {
+            String filePath = properties.getString ("filePath", null);
+            if (filePath == null) {
+                return null;
+            }
+            b = CPPLiteBreakpoint.create(filePath, lineNumber);
+        }
         b.setGroupName(
             properties.getString (Breakpoint.PROP_GROUP_NAME, "")
         );
@@ -79,18 +87,21 @@ public class BreakpointsReader implements Properties.Reader {
             b.disable ();
         return b;
     }
-    
+
     @Override
     public void write (Object object, Properties properties) {
         CPPLiteBreakpoint b = (CPPLiteBreakpoint) object;
-        FileObject fo = (FileObject) b.getLine().getLookup().lookup(FileObject.class);
-        properties.setString("url", fo.toURL().toString());
+        FileObject fo = b.getFileObject();
+        if (fo != null) {
+            properties.setString("url", fo.toURL().toString());
+        }
+        properties.setString("filePath", b.getFilePath());
         properties.setInt (
-            "lineNumber", 
-            b.getLine ().getLineNumber ()
+            "lineNumber",
+            b.getLineNumber() - 1
         );
         properties.setString (
-            Breakpoint.PROP_GROUP_NAME, 
+            Breakpoint.PROP_GROUP_NAME,
             b.getGroupName ()
         );
         properties.setBoolean (Breakpoint.PROP_ENABLED, b.isEnabled ());
@@ -103,32 +114,4 @@ public class BreakpointsReader implements Properties.Reader {
         }
         properties.setString(CPPLiteBreakpoint.PROP_CONDITION, condition);
     }
-    
-
-    private Line getLine (String url, int lineNumber) {
-        FileObject file;
-        try {
-            file = URLMapper.findFileObject (new URL (url));
-        } catch (MalformedURLException e) {
-            return null;
-        }
-        if (file == null) return null;
-        DataObject dataObject;
-        try {
-            dataObject = DataObject.find (file);
-        } catch (DataObjectNotFoundException ex) {
-            return null;
-        }
-        if (dataObject == null) return null;
-        LineCookie lineCookie = dataObject.getLookup().lookup(LineCookie.class);
-        if (lineCookie == null) return null;
-        Line.Set ls = lineCookie.getLineSet ();
-        if (ls == null) return null;
-        try {
-            return ls.getCurrent (lineNumber);
-        } catch (IndexOutOfBoundsException e) {
-        } catch (IllegalArgumentException e) {
-        }
-        return null;
-    }
 }
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/CPPLiteBreakpoint.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/CPPLiteBreakpoint.java
index 6f063ce..f4e6eb0 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/CPPLiteBreakpoint.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/CPPLiteBreakpoint.java
@@ -19,12 +19,15 @@
 
 package org.netbeans.modules.cpplite.debugger.breakpoints;
 
+import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NullAllowed;
 import org.netbeans.api.debugger.Breakpoint;
 import org.netbeans.api.debugger.DebuggerEngine;
 import org.netbeans.api.debugger.DebuggerManager;
@@ -33,7 +36,11 @@ import org.netbeans.api.debugger.DebuggerManagerListener;
 import org.netbeans.api.project.Project;
 import org.netbeans.api.project.ProjectManager;
 import org.netbeans.modules.cpplite.debugger.CPPLiteDebugger;
+import org.openide.cookies.LineCookie;
 import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.loaders.DataObject;
+import org.openide.loaders.DataObjectNotFoundException;
 import org.openide.text.Line;
 import org.openide.util.NbBundle;
 import org.openide.util.WeakListeners;
@@ -47,28 +54,101 @@ import org.openide.util.WeakListeners;
 public final class CPPLiteBreakpoint extends Breakpoint {
 
     public static final String PROP_CONDITION = "condition";                    // NOI18N
+    public static final String PROP_HIDDEN = "hidden";                          // NOI18N
 
-    private volatile boolean enabled = true;
-    private final Map<CPPLiteDebugger, String> ids = new HashMap<>();
-    private final Line line;
+    private final AtomicBoolean enabled = new AtomicBoolean(true);
+    private final AtomicBoolean hidden = new AtomicBoolean(false);
+    @NullAllowed
+    private final FileObject fileObject; // The user file that contains the breakpoint
+    private final String filePath; // Path of the file to which MI breakpoint is submitted
+    private final int lineNumber; // The breakpoint line number
     private volatile String condition;
 
-    public CPPLiteBreakpoint (Line line) {
-        this.line = line;
+    private CPPLiteBreakpoint (FileObject fileObject, String filePath, int lineNumber) {
+        this.fileObject = fileObject;
+        this.filePath = filePath;
+        this.lineNumber = lineNumber;
+    }
+
+    public static CPPLiteBreakpoint create(Line line) {
+        int lineNumber = line.getLineNumber() + 1;
+        FileObject fileObject = line.getLookup().lookup(FileObject.class);
+        String filePath = FileUtil.toFile(fileObject).getAbsolutePath();
+        return new CPPLiteBreakpoint(fileObject, filePath, lineNumber);
     }
 
-    public Line getLine () {
-        return line;
+    /**
+     * Create a new CPP lite breakpoint based on a user file.
+     * @param fileObject the file path of the breakpoint
+     * @param lineNumber 1-based line number
+     * @return a new breakpoint.
+     */
+    public static CPPLiteBreakpoint create(FileObject fileObject, int lineNumber) {
+        String filePath = FileUtil.toFile(fileObject).getAbsolutePath();
+        return new CPPLiteBreakpoint(fileObject, filePath, lineNumber);
     }
 
     /**
+     * Create a new CPP lite breakpoint, that is not associated with a user file.
+     * @param filePath the file path of the breakpoint in the debuggee
+     * @param lineNumber 1-based line number
+     * @return a new breakpoint.
+     */
+    public static CPPLiteBreakpoint create(String filePath, int lineNumber) {
+        return new CPPLiteBreakpoint(null, filePath, lineNumber);
+    }
+
+    /**
+     * Get the file path of the breakpoint in the debuggee.
+     */
+    public String getFilePath() {
+        return filePath;
+    }
+
+    /**
+     * 1-based line number.
+     */
+    public int getLineNumber() {
+        return lineNumber;
+    }
+
+    @CheckForNull
+    public FileObject getFileObject() {
+        return fileObject;
+    }
+
+    @CheckForNull
+    public Line getLine() {
+        FileObject fo = fileObject;
+        if (fo == null) {
+            return null;
+        }
+        DataObject dataObject;
+        try {
+            dataObject = DataObject.find(fo);
+        } catch (DataObjectNotFoundException ex) {
+            return null;
+        }
+        LineCookie lineCookie = dataObject.getLookup().lookup(LineCookie.class);
+        if (lineCookie != null) {
+            Line.Set ls = lineCookie.getLineSet ();
+            if (ls != null) {
+                try {
+                    return ls.getCurrent(lineNumber - 1);
+                } catch (IndexOutOfBoundsException | IllegalArgumentException e) {
+                }
+            }
+        }
+        return null;
+    }
+    /**
      * Test whether the breakpoint is enabled.
      *
      * @return <code>true</code> if so
      */
     @Override
     public boolean isEnabled () {
-        return enabled;
+        return enabled.get();
     }
 
     /**
@@ -76,9 +156,9 @@ public final class CPPLiteBreakpoint extends Breakpoint {
      */
     @Override
     public void disable () {
-        if (!enabled) return;
-        enabled = false;
-        firePropertyChange (PROP_ENABLED, Boolean.TRUE, Boolean.FALSE);
+        if (enabled.compareAndSet(true, false)) {
+            firePropertyChange (PROP_ENABLED, Boolean.TRUE, Boolean.FALSE);
+        }
     }
 
     /**
@@ -86,9 +166,9 @@ public final class CPPLiteBreakpoint extends Breakpoint {
      */
     @Override
     public void enable () {
-        if (enabled) return;
-        enabled = true;
-        firePropertyChange (PROP_ENABLED, Boolean.FALSE, Boolean.TRUE);
+        if (enabled.compareAndSet(false, true)) {
+            firePropertyChange (PROP_ENABLED, Boolean.FALSE, Boolean.TRUE);
+        }
     }
 
     /**
@@ -117,6 +197,27 @@ public final class CPPLiteBreakpoint extends Breakpoint {
         setValidity(validity, reason);
     }
 
+    /**
+     * Gets value of hidden property.
+     *
+     * @return value of hidden property
+     */
+    public boolean isHidden() {
+        return hidden.get();
+    }
+
+    /**
+     * Sets value of hidden property.
+     *
+     * @param h a new value of hidden property
+     */
+    public void setHidden(boolean h) {
+        boolean old = hidden.getAndSet(h);
+        if (old != h) {
+            firePropertyChange(PROP_HIDDEN, old, h);
+        }
+    }
+
     @Override
     public GroupProperties getGroupProperties() {
         return new CPPGroupProperties();
@@ -137,7 +238,7 @@ public final class CPPLiteBreakpoint extends Breakpoint {
         }
 
         private FileObject getFile() {
-            return line.getLookup().lookup(FileObject.class);
+            return FileUtil.toFileObject(new File(filePath));
         }
 
         @Override
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/CPPLiteBreakpointActionProvider.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/CPPLiteBreakpointActionProvider.java
index c166e25..2402fe0 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/CPPLiteBreakpointActionProvider.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/CPPLiteBreakpointActionProvider.java
@@ -69,23 +69,30 @@ public class CPPLiteBreakpointActionProvider extends ActionsProviderSupport
     @Override
     public void doAction (Object action) {
         Line line = getCurrentLine ();
-        if (line == null) return ;
-        Breakpoint[] breakpoints = DebuggerManager.getDebuggerManager ().
-            getBreakpoints ();
+        if (line == null) {
+            return ;
+        }
+        Breakpoint[] breakpoints = DebuggerManager.getDebuggerManager().getBreakpoints ();
+        FileObject fo = line.getLookup().lookup(FileObject.class);
+        if (fo == null) {
+            return ;
+        }
+        int lineNumber = line.getLineNumber() + 1;
         int i, k = breakpoints.length;
-        for (i = 0; i < k; i++)
-            if ( breakpoints [i] instanceof CPPLiteBreakpoint &&
-                 ((CPPLiteBreakpoint) breakpoints [i]).getLine ().equals (line)
-            ) {
-                DebuggerManager.getDebuggerManager ().removeBreakpoint
-                    (breakpoints [i]);
-                break;
+        for (i = 0; i < k; i++) {
+            if (breakpoints[i] instanceof CPPLiteBreakpoint) {
+                CPPLiteBreakpoint cppb = (CPPLiteBreakpoint) breakpoints[i];
+                if (fo.equals(cppb.getFileObject()) && cppb.getLineNumber() == lineNumber) {
+                    DebuggerManager.getDebuggerManager().removeBreakpoint(cppb);
+                    break;
+                }
             }
-        if (i == k)
+        }
+        if (i == k) {
             DebuggerManager.getDebuggerManager ().addBreakpoint (
-                new CPPLiteBreakpoint (line)
+                CPPLiteBreakpoint.create(line)
             );
-        //S ystem.out.println("toggle");
+        }
     }
 
     /**
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/PersistenceManager.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/PersistenceManager.java
index d467600..d77c8f2 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/PersistenceManager.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/breakpoints/PersistenceManager.java
@@ -136,12 +136,15 @@ public class PersistenceManager implements LazyDebuggerManagerListener {
     private static Breakpoint[] getBreakpoints () {
         Breakpoint[] bs = DebuggerManager.getDebuggerManager ().
             getBreakpoints ();
-        int i, k = bs.length;
         List<Breakpoint> bb = new ArrayList<>();
-        for (i = 0; i < k; i++)
-            // Don't store hidden breakpoints
-            if (bs[i] instanceof CPPLiteBreakpoint)
-                bb.add (bs [i]);
+        for (Breakpoint b : bs) {
+            if (b instanceof CPPLiteBreakpoint) {
+                // Don't store hidden breakpoints
+                if (!((CPPLiteBreakpoint) b).isHidden()) {
+                    bb.add(b);
+                }
+            }
+        }
         bs = new Breakpoint [bb.size ()];
         return (Breakpoint[]) bb.toArray (bs);
     }
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/debuggingview/DebuggingModel.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/debuggingview/DebuggingModel.java
index 1db0f44..c86d1d5 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/debuggingview/DebuggingModel.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/debuggingview/DebuggingModel.java
@@ -128,7 +128,7 @@ public class DebuggingModel extends CachedChildrenTreeModel implements ExtendedN
             return ((CPPThread) node).getName();
         } else if (node instanceof CPPFrame) {
             CPPFrame frame = (CPPFrame) node;
-            return frame.functionName + "; " + frame.shortFileName + ":" + frame.line;
+            return frame.getName();
         }
         throw new UnknownTypeException (node);
     }
@@ -143,7 +143,7 @@ public class DebuggingModel extends CachedChildrenTreeModel implements ExtendedN
             return details;
         } else if (node instanceof CPPFrame) {
             CPPFrame frame = (CPPFrame) node;
-            return frame.functionName + "; " + frame.fullFileName + ":" + frame.line;
+            return frame.getDescription();
         }
         throw new UnknownTypeException (node);
     }
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/models/CallStackModel.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/models/CallStackModel.java
index 017bef6..30768d9 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/models/CallStackModel.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/models/CallStackModel.java
@@ -19,6 +19,8 @@
 
 package org.netbeans.modules.cpplite.debugger.models;
 
+import java.net.MalformedURLException;
+import java.net.URI;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
 import javax.swing.Action;
@@ -39,6 +41,9 @@ import org.netbeans.spi.viewmodel.TreeModel;
 import org.netbeans.spi.viewmodel.ModelListener;
 import org.netbeans.spi.viewmodel.UnknownTypeException;
 import org.netbeans.spi.debugger.ui.Constants;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.filesystems.URLMapper;
 
 import org.openide.text.Line;
 import org.openide.util.NbBundle;
@@ -190,7 +195,7 @@ public class CallStackModel implements TreeModel, NodeModel, NodeActionsProvider
     public String getDisplayName (Object node) throws UnknownTypeException {
         if (node instanceof CPPFrame) {
             CPPFrame frame = (CPPFrame) node;
-            return frame.functionName + "; " + frame.shortFileName + ":" + frame.line;
+            return frame.getName();
         }
         if (node == ROOT) {
             return ROOT;
@@ -232,7 +237,7 @@ public class CallStackModel implements TreeModel, NodeModel, NodeActionsProvider
     public String getShortDescription (Object node) throws UnknownTypeException {
         if (node instanceof CPPFrame) {
             CPPFrame frame = (CPPFrame) node;
-            return frame.functionName + "; " + frame.fullFileName + ":" + frame.line;
+            return frame.getDescription();
         }
         throw new UnknownTypeException (node);
     }
@@ -295,7 +300,23 @@ public class CallStackModel implements TreeModel, NodeModel, NodeActionsProvider
         if (columnID == Constants.CALL_STACK_FRAME_LOCATION_COLUMN_ID) {
             if (node instanceof CPPFrame) {
                 CPPFrame frame = (CPPFrame) node;
-                return frame.fullFileName + ":" + frame.line;
+                URI sourceURI = frame.getSourceURI();
+                if (sourceURI == null) {
+                    return "";
+                }
+                String sourceName;
+                try {
+                    FileObject file = URLMapper.findFileObject(sourceURI.toURL());
+                    sourceName = file.getPath();
+                } catch (MalformedURLException ex) {
+                    sourceName = sourceURI.toString();
+                }
+                int line = frame.getLine();
+                if (line > 0) {
+                    return sourceName + ':' + line;
+                } else {
+                    return sourceName + ":?";
+                }
             }
         }
         throw new UnknownTypeException (node);
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/models/VariablesModel.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/models/VariablesModel.java
index cc0666b..ed47d5a 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/models/VariablesModel.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/models/VariablesModel.java
@@ -27,7 +27,7 @@ import org.netbeans.modules.cpplite.debugger.CPPFrame;
 import org.netbeans.modules.cpplite.debugger.CPPLiteDebugger;
 import org.netbeans.modules.cpplite.debugger.CPPLiteDebugger.StateListener;
 import org.netbeans.modules.cpplite.debugger.CPPThread;
-import org.netbeans.modules.cpplite.debugger.CPPVariable;
+import org.netbeans.modules.nativeimage.api.debug.NIVariable;
 
 import org.netbeans.spi.debugger.ContextProvider;
 import org.netbeans.spi.debugger.DebuggerServiceRegistration;
@@ -40,6 +40,7 @@ import org.netbeans.spi.viewmodel.UnknownTypeException;
 import org.openide.util.NbBundle;
 
 import org.openide.util.WeakListeners;
+import org.netbeans.modules.nativeimage.spi.debug.filters.VariableDisplayer;
 
 /**
  *
@@ -56,12 +57,14 @@ public class VariablesModel implements TreeModel, NodeModel, TableModel, StateLi
 
     private final CPPLiteDebugger       debugger;
     private final List<ModelListener>   listeners = new CopyOnWriteArrayList<>();
+    private final VariableDisplayer    displayer;
     private volatile CPPFrame           currentFrame;
 
 
     public VariablesModel (ContextProvider contextProvider) {
         debugger = contextProvider.lookupFirst(null, CPPLiteDebugger.class);
         debugger.addStateListener(WeakListeners.create(StateListener.class, this, debugger));
+        displayer = contextProvider.lookupFirst(null, VariableDisplayer.class);
         currentFrame = debugger.getCurrentFrame();
     }
 
@@ -96,24 +99,31 @@ public class VariablesModel implements TreeModel, NodeModel, TableModel, StateLi
      */
     @Override
     public Object[] getChildren (Object parent, int from, int to) throws UnknownTypeException {
-        CPPVariable parentVar;
+        NIVariable parentVar;
         if (parent == ROOT) {
             parentVar = null;
-        } else if (parent instanceof CPPVariable) {
-            parentVar = (CPPVariable) parent;
+        } else if (parent instanceof NIVariable) {
+            parentVar = (NIVariable) parent;
         } else {
             throw new UnknownTypeException (parent);
         }
         CPPFrame frame = currentFrame;
         if (frame != null) {
-            Map<String, CPPVariable> variables = (parentVar == null) ? frame.getVariables() : parentVar.getChildrenVariables();
-            Object[] array = variables.values().toArray();
-            if (array.length == 1 && array[0] == null) {
-                // Some error / message
-                return new Object[]{variables.keySet().iterator().next()};
+            NIVariable[] array;
+            if (parentVar == null) {
+                Map<String, NIVariable> variables = frame.getVariables();
+                array = variables.values().toArray(new NIVariable[0]);
+                if (array.length == 1 && array[0] == null) {
+                    // Some error / message
+                    return new Object[]{variables.keySet().iterator().next()};
+                }
             } else {
-                return array;
+                array = parentVar.getChildren(from, to);
             }
+            if (displayer != null) {
+                array = displayer.displayed(array);
+            }
+            return array;
         } else {
             return NO_VARS;
         }
@@ -134,8 +144,8 @@ public class VariablesModel implements TreeModel, NodeModel, TableModel, StateLi
         if (node instanceof String) {
             return true;
         }
-        if (node instanceof CPPVariable) {
-            return ((CPPVariable) node).getNumChildren() == 0;
+        if (node instanceof NIVariable) {
+            return ((NIVariable) node).getNumChildren() == 0;
         }
         throw new UnknownTypeException (node);
     }
@@ -158,8 +168,8 @@ public class VariablesModel implements TreeModel, NodeModel, TableModel, StateLi
     public int getChildrenCount (Object node) throws UnknownTypeException {
         if (node == ROOT) {
             return Integer.MAX_VALUE;
-        } else if (node instanceof CPPVariable) {
-            return ((CPPVariable) node).getNumChildren();
+        } else if (node instanceof NIVariable) {
+            return ((NIVariable) node).getNumChildren();
         }
         throw new UnknownTypeException (node);
     }
@@ -201,8 +211,8 @@ public class VariablesModel implements TreeModel, NodeModel, TableModel, StateLi
         if (node instanceof String) {
             return (String) node;
         }
-        if (node instanceof CPPVariable) {
-            return ((CPPVariable) node).getName();
+        if (node instanceof NIVariable) {
+            return ((NIVariable) node).getName();
         }
         throw new UnknownTypeException (node);
     }
@@ -218,7 +228,7 @@ public class VariablesModel implements TreeModel, NodeModel, TableModel, StateLi
      */
     @Override
     public String getIconBase (Object node) throws UnknownTypeException {
-        if (node instanceof CPPVariable) {
+        if (node instanceof NIVariable) {
             return LOCAL;
         }
         if (node instanceof String) {
@@ -265,13 +275,13 @@ public class VariablesModel implements TreeModel, NodeModel, TableModel, StateLi
     @Override
     public Object getValueAt (Object node, String columnID) throws UnknownTypeException {
         if (columnID.equals ("LocalsValue")) {
-            if (node instanceof CPPVariable) {
-                return ((CPPVariable) node).getValue();
+            if (node instanceof NIVariable) {
+                return ((NIVariable) node).getValue();
             }
         }
         if (columnID.equals ("LocalsType")) {
-            if (node instanceof CPPVariable) {
-                return ((CPPVariable) node).getType();
+            if (node instanceof NIVariable) {
+                return ((NIVariable) node).getType();
             }
         }
         if (node instanceof String) {
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/models/WatchesModel.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/models/WatchesModel.java
index abf9740..56707c4 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/models/WatchesModel.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/models/WatchesModel.java
@@ -32,8 +32,8 @@ import org.netbeans.modules.cpplite.debugger.CPPFrame;
 import org.netbeans.modules.cpplite.debugger.CPPLiteDebugger;
 import org.netbeans.modules.cpplite.debugger.CPPLiteDebugger.StateListener;
 import org.netbeans.modules.cpplite.debugger.CPPThread;
-import org.netbeans.modules.cpplite.debugger.CPPVariable;
-import org.netbeans.modules.cpplite.debugger.EvaluateException;
+import org.netbeans.modules.nativeimage.api.debug.EvaluateException;
+import org.netbeans.modules.nativeimage.api.debug.NIVariable;
 import org.netbeans.spi.debugger.ContextProvider;
 import org.netbeans.spi.debugger.DebuggerServiceRegistration;
 import org.netbeans.spi.viewmodel.ModelEvent;
@@ -99,7 +99,7 @@ public class WatchesModel implements TreeModelFilter, NodeModelFilter, TableMode
         if (ew != null) {
             switch (ew.getStatus()) {
                 case READY:
-                    CPPVariable result = ew.getResult();
+                    NIVariable result = ew.getResult();
                     return result.getNumChildren();
             }
         }
@@ -115,7 +115,7 @@ public class WatchesModel implements TreeModelFilter, NodeModelFilter, TableMode
         if (ew != null) {
             switch (ew.getStatus()) {
                 case READY:
-                    CPPVariable result = ew.getResult();
+                    NIVariable result = ew.getResult();
                     return result.getNumChildren() == 0;
             }
         }
@@ -171,7 +171,7 @@ public class WatchesModel implements TreeModelFilter, NodeModelFilter, TableMode
             ew.startEvaluate();
             switch (ew.getStatus()) {
                 case READY:
-                    CPPVariable result = ew.getResult();
+                    NIVariable result = ew.getResult();
                     return ew.getExpression() + " = " + result.getValue();
                 case FAILED:
                     EvaluateException exc = ew.getException();
@@ -212,7 +212,7 @@ public class WatchesModel implements TreeModelFilter, NodeModelFilter, TableMode
                 ew.startEvaluate();
                 switch (ew.getStatus()) {
                     case READY:
-                        CPPVariable result = ew.getResult();
+                        NIVariable result = ew.getResult();
                         if (showValue) {
                             return result.getValue();
                         } else {
@@ -342,7 +342,7 @@ public class WatchesModel implements TreeModelFilter, NodeModelFilter, TableMode
         private final Watch watch;
         private volatile AtomicReference<EvalStatus> status = new AtomicReference<>(EvalStatus.NEW);
         private volatile String expression;
-        private volatile CPPVariable result;
+        private volatile NIVariable result;
         private volatile EvaluateException exception;
 
         private EvalWatch(Watch watch) {
@@ -364,16 +364,17 @@ public class WatchesModel implements TreeModelFilter, NodeModelFilter, TableMode
                 exception = null;
                 String expression = watch.getExpression();
                 this.expression = expression;
-                frame.evaluateLazy(expression,
-                                   (CPPVariable variable) -> {
+                frame.evaluateAsync(expression).thenAccept(
+                                   (NIVariable variable) -> {
                                        result = variable;
                                        status.set(EvalStatus.READY);
                                        fireChanged(watch);
-                                   },
-                                   (EvaluateException exc) -> {
-                                       exception = exc;
+                                   }).exceptionally(
+                                   exc -> {
+                                       exception = (EvaluateException) exc;
                                        status.set(EvalStatus.FAILED);
                                        fireChanged(watch);
+                                       return null;
                                    });
             }
         }
@@ -382,7 +383,7 @@ public class WatchesModel implements TreeModelFilter, NodeModelFilter, TableMode
             return expression;
         }
 
-        CPPVariable getResult() {
+        NIVariable getResult() {
             return result;
         }
 
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/ni/NIBreakpoints.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/ni/NIBreakpoints.java
new file mode 100644
index 0000000..754ccc6
--- /dev/null
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/ni/NIBreakpoints.java
@@ -0,0 +1,85 @@
+/*
+ * 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.cpplite.debugger.ni;
+
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.netbeans.api.debugger.Breakpoint;
+import org.netbeans.api.debugger.DebuggerManager;
+import org.netbeans.modules.cpplite.debugger.breakpoints.CPPLiteBreakpoint;
+import org.netbeans.modules.nativeimage.api.debug.NILineBreakpointDescriptor;
+
+/**
+ *
+ * @author martin
+ */
+final class NIBreakpoints {
+
+    private final Map<Object, CPPLiteBreakpoint> ni2C = new IdentityHashMap<>();
+
+    Breakpoint addLineBreakpoint(Object key, NILineBreakpointDescriptor bd) {
+        CPPLiteBreakpoint breakpoint;
+        boolean isNew;
+        synchronized (ni2C) {
+            breakpoint = ni2C.get(key);
+            isNew = breakpoint == null;
+            if (isNew) {
+                breakpoint = CPPLiteBreakpoint.create(bd.getFilePath(), bd.getLine());
+                ni2C.put(key, breakpoint);
+            }
+        }
+        // TODO Update fileUrl and line number
+        if (bd.isEnabled()) {
+            breakpoint.enable();
+        } else {
+            breakpoint.disable();
+        }
+        breakpoint.setCondition(bd.getCondition());
+        breakpoint.setHidden(bd.isHidden());
+        if (isNew) {
+            DebuggerManager.getDebuggerManager().addBreakpoint(breakpoint);
+        }
+        return breakpoint;
+    }
+
+    void removeBreakpoint(Object key) {
+        CPPLiteBreakpoint breakpoint;
+        synchronized (ni2C) {
+            breakpoint = ni2C.remove(key);
+        }
+        if (breakpoint != null) {
+            DebuggerManager.getDebuggerManager().removeBreakpoint(breakpoint);
+        }
+    }
+
+    void dispose() {
+        List<CPPLiteBreakpoint> cbs;
+        synchronized (ni2C) {
+            cbs = new ArrayList<>(ni2C.size());
+            cbs.addAll(ni2C.values());
+            ni2C.clear();
+        }
+        for (CPPLiteBreakpoint cb : cbs) {
+            DebuggerManager.getDebuggerManager().removeBreakpoint(cb);
+        }
+    }
+}
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/ni/NIDebuggerProviderImpl.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/ni/NIDebuggerProviderImpl.java
new file mode 100644
index 0000000..f051d93
--- /dev/null
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/ni/NIDebuggerProviderImpl.java
@@ -0,0 +1,172 @@
+/*
+ * 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.cpplite.debugger.ni;
+
+import java.io.File;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+
+import org.netbeans.api.debugger.Breakpoint;
+import org.netbeans.api.debugger.DebuggerEngine;
+import org.netbeans.api.extexecution.ExecutionDescriptor;
+import org.netbeans.api.extexecution.ExecutionService;
+import org.netbeans.modules.cpplite.debugger.CPPFrame;
+import org.netbeans.modules.cpplite.debugger.CPPLiteDebugger;
+import org.netbeans.modules.cpplite.debugger.CPPLiteDebuggerConfig;
+import org.netbeans.modules.cpplite.debugger.CPPThread;
+import org.netbeans.modules.nativeimage.api.debug.EvaluateException;
+import org.netbeans.modules.nativeimage.api.debug.NIFrame;
+import org.netbeans.modules.nativeimage.api.debug.NILineBreakpointDescriptor;
+import org.netbeans.modules.nativeimage.api.debug.NIVariable;
+import org.netbeans.modules.nativeimage.spi.debug.NIDebuggerProvider;
+import org.netbeans.modules.nativeimage.spi.debug.filters.FrameDisplayer;
+
+import org.openide.LifecycleManager;
+import org.openide.util.Pair;
+import org.openide.util.RequestProcessor;
+import org.netbeans.modules.nativeimage.spi.debug.filters.VariableDisplayer;
+
+/**
+ *
+ * @author martin
+ */
+public class NIDebuggerProviderImpl implements NIDebuggerProvider {
+
+    private final NIBreakpoints breakpointsHandler = new NIBreakpoints();
+    private volatile CPPLiteDebugger debugger;
+    private volatile FrameDisplayer frameDisplayer;
+    private volatile VariableDisplayer variablesDisplayer;
+    private final RequestProcessor varDisplayerRP = new RequestProcessor(NIDebuggerProvider.class);
+
+    public NIDebuggerProviderImpl() {
+    }
+
+    @Override
+    public Breakpoint addLineBreakpoint(Object id, NILineBreakpointDescriptor breakpointDescriptor) {
+        return breakpointsHandler.addLineBreakpoint(id, breakpointDescriptor);
+    }
+
+    @Override
+    public void removeBreakpoint(Object id) {
+        breakpointsHandler.removeBreakpoint(id);
+    }
+
+    @Override
+    public void setFrameDisplayer(FrameDisplayer frameDisplayer) {
+        this.frameDisplayer = frameDisplayer;
+    }
+
+    @Override
+    public void setVariablesDisplayer(VariableDisplayer variablesDisplayer) {
+        this.variablesDisplayer = variablesDisplayer;
+    }
+
+    @Override
+    public CompletableFuture<Void> start(List<String> command, File workingDirectory, String miDebugger, String displayName, ExecutionDescriptor executionDescriptor, Consumer<DebuggerEngine> startedEngine) {
+        if (debugger != null) {
+            throw new IllegalStateException("Debugger has started already.");
+        }
+        if (executionDescriptor == null) {
+            executionDescriptor = new ExecutionDescriptor()
+                .showProgress(true)
+                .showSuspended(true)
+                .frontWindowOnError(true)
+                .controllable(true);
+        }
+        CompletableFuture<Void> completed = new CompletableFuture<>();
+        ExecutionService.newService(() -> {
+            LifecycleManager.getDefault().saveAll();
+            Pair<DebuggerEngine, Process> engineProcess = CPPLiteDebugger.startDebugging(
+                    new CPPLiteDebuggerConfig(command, workingDirectory, miDebugger),
+                    frameDisplayer,
+                    variablesDisplayer);
+            DebuggerEngine engine = engineProcess.first();
+            CPPLiteDebugger debugger = engine.lookupFirst(null, CPPLiteDebugger.class);
+            this.debugger = debugger;
+            if (startedEngine != null) {
+                startedEngine.accept(engine);
+            }
+            debugger.addStateListener(new CPPLiteDebugger.StateListener() {
+                @Override
+                public void currentThread(CPPThread thread) {}
+
+                @Override
+                public void currentFrame(CPPFrame frame) {}
+
+                @Override
+                public void suspended(boolean suspended) {}
+
+                @Override
+                public void finished() {
+                    breakpointsHandler.dispose();
+                    completed.complete(null);
+                }
+            });
+            debugger.execRun();
+            return engineProcess.second();
+        }, executionDescriptor, displayName).run();
+        return completed;
+    }
+
+    @Override
+    public CompletableFuture<NIVariable> evaluateAsync(String expression, String resultName, NIFrame frame) {
+        CompletableFuture<NIVariable> result = new CompletableFuture<>();
+        CPPFrame cframe;
+        if (frame != null) {
+            CPPThread thread = debugger.getThreads().get(frame.getThreadId());
+            if (thread == null) {
+                result.completeExceptionally(new EvaluateException("No thread " + frame.getThreadId()));
+                return result;
+            }
+            try {
+                cframe = (CPPFrame) thread.getFrames().get(frame.getLevel());
+            } catch (IndexOutOfBoundsException ioobex) {
+                result.completeExceptionally(new EvaluateException(ioobex.getLocalizedMessage()));
+                return result;
+            }
+        } else {
+            cframe = debugger.getCurrentFrame();
+        }
+        cframe.evaluateAsync(expression, resultName).thenAccept(
+                rawResult -> {
+                    // We must not run the VariableDisplayer synchronously with the MI command thread
+                    varDisplayerRP.post(() -> {
+                        NIVariable[] variables = variablesDisplayer.displayed(rawResult);
+                        NIVariable variable = (variables.length > 0) ? variables[0] : rawResult;
+                        result.complete(variable);
+                    });
+                }).exceptionally(
+                exception -> {
+                    result.completeExceptionally(exception);
+                    return null;
+                });
+        return result;
+    }
+
+    @Override
+    public String readMemory(String address, long offset, int length) {
+        return debugger.readMemory(address, offset, length);
+    }
+
+    @Override
+    public String getVersion() {
+        return debugger.getVersion();
+    }
+}
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/EvaluateException.java b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/ni/NIDebuggerServiceProviderImpl.java
similarity index 61%
copy from cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/EvaluateException.java
copy to cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/ni/NIDebuggerServiceProviderImpl.java
index 0fc9c76..7a8be49 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/EvaluateException.java
+++ b/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/ni/NIDebuggerServiceProviderImpl.java
@@ -16,17 +16,22 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.netbeans.modules.cpplite.debugger;
+package org.netbeans.modules.cpplite.debugger.ni;
 
-public final class EvaluateException extends Exception {
+import org.netbeans.modules.nativeimage.spi.debug.NIDebuggerProvider;
+import org.netbeans.modules.nativeimage.spi.debug.NIDebuggerServiceProvider;
+import org.openide.util.lookup.ServiceProvider;
 
-    /**
-     * Constructs an instance of <code>EvaluateException</code> with the
-     * specified detail message.
-     *
-     * @param msg the detail message.
-     */
-    EvaluateException(String msg) {
-        super(msg);
+/**
+ *
+ * @author martin
+ */
+@ServiceProvider(service = NIDebuggerServiceProvider.class)
+public class NIDebuggerServiceProviderImpl implements NIDebuggerServiceProvider {
+
+    @Override
+    public NIDebuggerProvider create() {
+        return new NIDebuggerProviderImpl();
     }
+
 }
diff --git a/cpplite/cpplite.debugger/test/unit/src/org/netbeans/modules/cpplite/debugger/AbstractDebugTest.java b/cpplite/cpplite.debugger/test/unit/src/org/netbeans/modules/cpplite/debugger/AbstractDebugTest.java
index 2d9d4bd..eb1e626 100644
--- a/cpplite/cpplite.debugger/test/unit/src/org/netbeans/modules/cpplite/debugger/AbstractDebugTest.java
+++ b/cpplite/cpplite.debugger/test/unit/src/org/netbeans/modules/cpplite/debugger/AbstractDebugTest.java
@@ -51,7 +51,7 @@ public abstract class AbstractDebugTest extends NbTestCase {
     }
 
     protected final void startDebugging(String name, File wd) throws IOException {
-        engine = CPPLiteDebugger.startDebugging(new CPPLiteDebuggerConfig(Arrays.asList(new File(wd, name).getAbsolutePath()), wd)).first();
+        engine = CPPLiteDebugger.startDebugging(new CPPLiteDebuggerConfig(Arrays.asList(new File(wd, name).getAbsolutePath()), wd, "gdb")).first();
         debugger = engine.lookupFirst(null, CPPLiteDebugger.class);
         debugger.addStateListener(new CPPLiteDebugger.StateListener() {
             @Override
@@ -80,6 +80,7 @@ public abstract class AbstractDebugTest extends NbTestCase {
             public void currentFrame(CPPFrame frame) {
             }
         });
+        debugger.execRun();
     }
 
     protected final void waitSuspended(int count) throws InterruptedException {
diff --git a/cpplite/cpplite.debugger/test/unit/src/org/netbeans/modules/cpplite/debugger/BreakpointsTest.java b/cpplite/cpplite.debugger/test/unit/src/org/netbeans/modules/cpplite/debugger/BreakpointsTest.java
index f80bee8..d22f578 100644
--- a/cpplite/cpplite.debugger/test/unit/src/org/netbeans/modules/cpplite/debugger/BreakpointsTest.java
+++ b/cpplite/cpplite.debugger/test/unit/src/org/netbeans/modules/cpplite/debugger/BreakpointsTest.java
@@ -76,8 +76,8 @@ public class BreakpointsTest extends AbstractDebugTest {
         compileC("breakpoints", wd);
         LineCookie lc = DataObject.find(source).getLookup().lookup(LineCookie.class);
         assertNotNull(lc);
-        CPPLiteBreakpoint bp8 = new CPPLiteBreakpoint(lc.getLineSet().getCurrent(7));
-        CPPLiteBreakpoint bp9 = new CPPLiteBreakpoint(lc.getLineSet().getCurrent(8));
+        CPPLiteBreakpoint bp8 = CPPLiteBreakpoint.create(lc.getLineSet().getCurrent(7));
+        CPPLiteBreakpoint bp9 = CPPLiteBreakpoint.create(lc.getLineSet().getCurrent(8));
         bp9.disable();
         DebuggerManager.getDebuggerManager().addBreakpoint(bp8);
         DebuggerManager.getDebuggerManager().addBreakpoint(bp9);
@@ -98,7 +98,7 @@ public class BreakpointsTest extends AbstractDebugTest {
         waitSuspended(3);
         assertStoppedAt(source.toURI(), 9);
 
-        CPPLiteBreakpoint bp10 = new CPPLiteBreakpoint(lc.getLineSet().getCurrent(9));
+        CPPLiteBreakpoint bp10 = CPPLiteBreakpoint.create(lc.getLineSet().getCurrent(9));
         DebuggerManager.getDebuggerManager().addBreakpoint(bp10);
 
         engine.getActionsManager().doAction(ActionsManager.ACTION_CONTINUE);
@@ -116,7 +116,7 @@ public class BreakpointsTest extends AbstractDebugTest {
 
         bp10.disable();
 
-        bp8 = new CPPLiteBreakpoint(lc.getLineSet().getCurrent(7));
+        bp8 = CPPLiteBreakpoint.create(lc.getLineSet().getCurrent(7));
         DebuggerManager.getDebuggerManager().addBreakpoint(bp8);
 
         engine.getActionsManager().doAction(ActionsManager.ACTION_CONTINUE);
diff --git a/cpplite/cpplite.debugger/test/unit/src/org/netbeans/modules/cpplite/debugger/StepTest.java b/cpplite/cpplite.debugger/test/unit/src/org/netbeans/modules/cpplite/debugger/StepTest.java
index a2042dd..307af7a 100644
--- a/cpplite/cpplite.debugger/test/unit/src/org/netbeans/modules/cpplite/debugger/StepTest.java
+++ b/cpplite/cpplite.debugger/test/unit/src/org/netbeans/modules/cpplite/debugger/StepTest.java
@@ -72,7 +72,7 @@ public class StepTest extends AbstractDebugTest {
         compileCPP("main", wd);
         LineCookie lc = DataObject.find(source).getLookup().lookup(LineCookie.class);
         assertNotNull(lc);
-        DebuggerManager.getDebuggerManager().addBreakpoint(new CPPLiteBreakpoint(lc.getLineSet().getCurrent(4)));
+        DebuggerManager.getDebuggerManager().addBreakpoint(CPPLiteBreakpoint.create(lc.getLineSet().getCurrent(4)));
         startDebugging("main", wd);
 
         waitSuspended(1);
diff --git a/cpplite/cpplite.project/src/org/netbeans/modules/cpplite/project/ActionProviderImpl.java b/cpplite/cpplite.project/src/org/netbeans/modules/cpplite/project/ActionProviderImpl.java
index cdcc5dc..428f4a8 100644
--- a/cpplite/cpplite.project/src/org/netbeans/modules/cpplite/project/ActionProviderImpl.java
+++ b/cpplite/cpplite.project/src/org/netbeans/modules/cpplite/project/ActionProviderImpl.java
@@ -90,7 +90,7 @@ public class ActionProviderImpl implements ActionProvider {
         }, executionDescriptor, ProjectUtils.getInformation(prj).getDisplayName() + " - " + command).run();
     }
 
-    private String quote(String s) {
+    private static String quote(String s) {
         return s.replace("_", "_u_").replace(" ", "_s_");
     }
 
diff --git a/ide/ide.kit/nbproject/project.xml b/ide/ide.kit/nbproject/project.xml
index cdd6261..3b19e47 100644
--- a/ide/ide.kit/nbproject/project.xml
+++ b/ide/ide.kit/nbproject/project.xml
@@ -195,6 +195,13 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
+                    <code-name-base>org.netbeans.modules.nativeimage.api</code-name-base>
+                    <run-dependency>
+                        <release-version>0</release-version>
+                        <specification-version>0.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
                     <code-name-base>org.netbeans.modules.options.editor</code-name-base>
                     <run-dependency>
                         <release-version>1</release-version>
diff --git a/ide/nativeimage.api/build.xml b/ide/nativeimage.api/build.xml
new file mode 100644
index 0000000..13ce273
--- /dev/null
+++ b/ide/nativeimage.api/build.xml
@@ -0,0 +1,28 @@
+<?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.
+
+-->
+<!-- You may freely edit this file. See harness/README in the NetBeans platform -->
+<!-- for some information on what you could do (e.g. targets to override). -->
+<!-- If you delete this file and reopen the project it will be recreated. -->
+<project name="ide/nativeimage.api" default="build" basedir=".">
+    <description>Builds, tests, and runs the project org.netbeans.modules.nativeimage.api.</description>
+    <import file="../../nbbuild/templates/projectized.xml"/>
+</project>
diff --git a/ide/nativeimage.api/manifest.mf b/ide/nativeimage.api/manifest.mf
new file mode 100644
index 0000000..fdfa01c
--- /dev/null
+++ b/ide/nativeimage.api/manifest.mf
@@ -0,0 +1,6 @@
+Manifest-Version: 1.0
+AutoUpdate-Show-In-Client: false
+OpenIDE-Module: org.netbeans.modules.nativeimage.api/0
+OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/nativeimage/api/Bundle.properties
+OpenIDE-Module-Specification-Version: 0.1
+
diff --git a/ide/nativeimage.api/nbproject/project.properties b/ide/nativeimage.api/nbproject/project.properties
new file mode 100644
index 0000000..5137752
--- /dev/null
+++ b/ide/nativeimage.api/nbproject/project.properties
@@ -0,0 +1,19 @@
+# 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.source=1.8
+javac.compilerargs=-Xlint -Xlint:-serial
diff --git a/cpplite/cpplite.debugger/nbproject/project.xml b/ide/nativeimage.api/nbproject/project.xml
similarity index 55%
copy from cpplite/cpplite.debugger/nbproject/project.xml
copy to ide/nativeimage.api/nbproject/project.xml
index d360c06..f9f0149 100644
--- a/cpplite/cpplite.debugger/nbproject/project.xml
+++ b/ide/nativeimage.api/nbproject/project.xml
@@ -23,7 +23,7 @@
     <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.cpplite.debugger</code-name-base>
+            <code-name-base>org.netbeans.modules.nativeimage.api</code-name-base>
             <module-dependencies>
                 <dependency>
                     <code-name-base>org.netbeans.api.annotations.common</code-name-base>
@@ -40,15 +40,16 @@
                     <compile-dependency/>
                     <run-dependency>
                         <release-version>1</release-version>
-                        <specification-version>1.63</specification-version>
+                        <specification-version>1.67</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
-                    <code-name-base>org.netbeans.modules.dlight.nativeexecution</code-name-base>
+                    <code-name-base>org.netbeans.modules.extexecution</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
-                        <specification-version>1.48</specification-version>
+                        <release-version>2</release-version>
+                        <specification-version>1.59</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
@@ -61,32 +62,6 @@
                     </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.62</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.59</specification-version>
-                    </run-dependency>
-                </dependency>
-                <dependency>
-                    <code-name-base>org.openide.awt</code-name-base>
-                    <build-prerequisite/>
-                    <compile-dependency/>
-                    <run-dependency>
-                        <specification-version>7.76</specification-version>
-                    </run-dependency>
-                </dependency>
-                <dependency>
                     <code-name-base>org.openide.filesystems</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
@@ -95,43 +70,11 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
-                    <code-name-base>org.openide.loaders</code-name-base>
-                    <build-prerequisite/>
-                    <compile-dependency/>
-                    <run-dependency>
-                        <specification-version>7.76</specification-version>
-                    </run-dependency>
-                </dependency>
-                <dependency>
-                    <code-name-base>org.openide.modules</code-name-base>
-                    <build-prerequisite/>
-                    <compile-dependency/>
-                    <run-dependency>
-                        <specification-version>7.56</specification-version>
-                    </run-dependency>
-                </dependency>
-                <dependency>
-                    <code-name-base>org.openide.nodes</code-name-base>
-                    <build-prerequisite/>
-                    <compile-dependency/>
-                    <run-dependency>
-                        <specification-version>7.53</specification-version>
-                    </run-dependency>
-                </dependency>
-                <dependency>
-                    <code-name-base>org.openide.text</code-name-base>
-                    <build-prerequisite/>
-                    <compile-dependency/>
-                    <run-dependency>
-                        <specification-version>6.75</specification-version>
-                    </run-dependency>
-                </dependency>
-                <dependency>
                     <code-name-base>org.openide.util</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
-                        <specification-version>9.15</specification-version>
+                        <specification-version>9.19</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
@@ -142,22 +85,6 @@
                         <specification-version>8.41</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.16</specification-version>
-                    </run-dependency>
-                </dependency>
-                <dependency>
-                    <code-name-base>org.openide.windows</code-name-base>
-                    <build-prerequisite/>
-                    <compile-dependency/>
-                    <run-dependency>
-                        <specification-version>6.85</specification-version>
-                    </run-dependency>
-                </dependency>
             </module-dependencies>
             <test-dependencies>
                 <test-type>
@@ -186,13 +113,16 @@
                 </test-type>
             </test-dependencies>
             <friend-packages>
-                <friend>org.netbeans.modules.cpplite.project</friend>
-                <package>org.netbeans.modules.cpplite.debugger.api</package>
+                <friend>org.netbeans.modules.cpplite.debugger</friend>
+                <friend>org.netbeans.modules.gradle.java</friend>
+                <friend>org.netbeans.modules.java.lsp.server</friend>
+                <friend>org.netbeans.modules.java.nativeimage.debugger</friend>
+                <package>org.netbeans.modules.nativeimage.api</package>
+                <package>org.netbeans.modules.nativeimage.api.debug</package>
+                <package>org.netbeans.modules.nativeimage.spi</package>
+                <package>org.netbeans.modules.nativeimage.spi.debug</package>
+                <package>org.netbeans.modules.nativeimage.spi.debug.filters</package>
             </friend-packages>
-            <class-path-extension>
-                <runtime-relative-path>ext/cpplite-mi-f8f8250283be.jar</runtime-relative-path>
-                <binary-origin>external/cpplite-mi-f8f8250283be.jar</binary-origin>
-            </class-path-extension>
         </data>
     </configuration>
 </project>
diff --git a/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/Bundle.properties b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/Bundle.properties
new file mode 100644
index 0000000..139a1c1
--- /dev/null
+++ b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/Bundle.properties
@@ -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.
+
+# manifest's description entries
+OpenIDE-Module-Display-Category=Base IDE
+OpenIDE-Module-Name=Native Image API
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/EvaluateException.java b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/EvaluateException.java
similarity index 66%
copy from cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/EvaluateException.java
copy to ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/EvaluateException.java
index 0fc9c76..1d542bb 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/EvaluateException.java
+++ b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/EvaluateException.java
@@ -16,17 +16,35 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.netbeans.modules.cpplite.debugger;
+package org.netbeans.modules.nativeimage.api.debug;
 
-public final class EvaluateException extends Exception {
+/**
+ * Thrown when evaluation in the native debugger fails.
+ *
+ * @since 1.0
+ */
+public class EvaluateException extends Exception {
 
     /**
      * Constructs an instance of <code>EvaluateException</code> with the
      * specified detail message.
      *
      * @param msg the detail message.
+     * @since 1.0
      */
-    EvaluateException(String msg) {
+    public EvaluateException(String msg) {
         super(msg);
     }
+
+    /**
+     * Constructs an instance of <code>EvaluateException</code> from an
+     * existing exception.
+     *
+     * @param t exception.
+     * @since 1.0
+     */
+    public EvaluateException(Throwable t) {
+        super(t.getLocalizedMessage(), t);
+        
+    }
 }
diff --git a/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIDebugger.java b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIDebugger.java
new file mode 100644
index 0000000..f980655
--- /dev/null
+++ b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIDebugger.java
@@ -0,0 +1,211 @@
+/*
+ * 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.nativeimage.api.debug;
+
+import java.io.File;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+
+import org.netbeans.api.debugger.Breakpoint;
+import org.netbeans.api.debugger.DebuggerEngine;
+import org.netbeans.api.extexecution.ExecutionDescriptor;
+import org.netbeans.modules.nativeimage.spi.debug.NIDebuggerProvider;
+import org.netbeans.modules.nativeimage.spi.debug.NIDebuggerServiceProvider;
+import org.netbeans.modules.nativeimage.spi.debug.filters.FrameDisplayer;
+import org.openide.util.Lookup;
+import org.netbeans.modules.nativeimage.spi.debug.filters.VariableDisplayer;
+import org.openide.util.Exceptions;
+import org.openide.util.NbBundle;
+
+/**
+ * Representation of a native image debugger.
+ * @since 1.0
+ */
+public final class NIDebugger {
+
+    private final NIDebuggerProvider provider;
+
+    NIDebugger(NIDebuggerProvider provider) {
+        this.provider = provider;
+    }
+
+    /**
+     * Creates a builder of a new debugger instance.
+     *
+     * @return a builder of a new debugger instance
+     * @throws IllegalStateException when the native debugger is not available
+     *         (there is not an implementation of {@link NIDebuggerServiceProvider}
+     *         registered in the default lookup).
+     * @since 1.0
+     */
+    @NbBundle.Messages({"MSG_NoNativeDebug=No native debugger is available. Please install native debugger module."})
+    public static Builder newBuilder() throws IllegalStateException {
+        NIDebuggerServiceProvider provider = Lookup.getDefault().lookup(NIDebuggerServiceProvider.class);
+        if (provider == null) {
+            throw Exceptions.attachLocalizedMessage(new IllegalStateException(), Bundle.MSG_NoNativeDebug());
+        } else {
+            return new Builder(provider.create());
+        }
+    }
+
+    /**
+     * Add or change a line breakpoint into the debugger.
+     * A breakpoint is added when the `id` is used for the first time and modified
+     * when breakpoint with that `id` was added already.
+     *
+     * @param id a unique ID of the breakpoint
+     * @param breakpointDescriptor the breakpoint descriptor
+     * @return an instance of the native breakpoint
+     * @since 1.0
+     */
+    public Breakpoint addLineBreakpoint(Object id, NILineBreakpointDescriptor breakpointDescriptor) {
+        Breakpoint breakpoint = provider.addLineBreakpoint(id, breakpointDescriptor);
+        assert breakpoint != null;
+        return breakpoint;
+    }
+
+    /**
+     * Remove breakpoint with the given id.
+     *
+     * @param id the ID of the breakpoint to remove
+     * @since 1.0
+     */
+    public void removeBreakpoint(Object id) {
+        provider.removeBreakpoint(id);
+    }
+
+    /**
+     * Start the actual debugging session. Call this typically after breakpoints are added.
+     *
+     * @param command a command to run the native image
+     * @param workingDirectory working directory
+     * @param debugger the native debugger command
+     * @param displayName display name of the debugger task
+     * @param executionDescriptor execution descriptor that describes the runtime attributes
+     * @param startedEngine the corresponding DebuggerEngine is passed to this consumer
+     * @return future that completes on the execution finish
+     * @since 1.0
+     */
+    public CompletableFuture<Void> start(List<String> command, File workingDirectory, String debugger, String displayName, ExecutionDescriptor executionDescriptor, Consumer<DebuggerEngine> startedEngine) {
+        return provider.start(command, workingDirectory, debugger, displayName, executionDescriptor, startedEngine);
+    }
+
+    /**
+     * An asynchronous expression evaluation.
+     *
+     * @param expression the expression to evaluate
+     * @param resultName preferred name of the result variable,
+     *                   when <code>null</code> the expression is used as the name
+     * @param frame the frame to evaluate at
+     * @return the completable future with the evaluation result
+     * @since 1.0
+     */
+    public CompletableFuture<NIVariable> evaluateAsync(String expression, String resultName, NIFrame frame) {
+        return provider.evaluateAsync(expression, resultName, frame);
+    }
+
+    /**
+     * A synchronous expression evaluation. Delegates to the asynchronous evaluation
+     * and wait for it's result.
+     *
+     * @param expression the expression to evaluate
+     * @param resultName preferred name of the result variable,
+     *                   when <code>null</code> the expression is used as the name
+     * @param frame the frame to evaluate at
+     * @return the evaluation result
+     * @throws EvaluateException when evaluation fails
+     * @since 1.0
+     */
+    public NIVariable evaluate(String expression, String resultName, NIFrame frame) throws EvaluateException {
+        try {
+            return provider.evaluateAsync(expression, resultName, frame).get();
+        } catch (ExecutionException | InterruptedException ex) {
+            throw new EvaluateException(ex.getCause());
+        }
+    }
+
+    /**
+     * Read data from memory.
+     *
+     * @param address address where to read the data from
+     * @param offset offset relative to the address where to start reading
+     * @param length number of bytes to read
+     * @return hexadecimal representation of the memory content, or <code>null</code>
+     *         when the read is not successful
+     * @since 1.0
+     */
+    public String readMemory(String address, long offset, int length) {
+        return provider.readMemory(address, offset, length);
+    }
+
+    /**
+     * Get version of the underlying native debugger.
+     *
+     * @since 1.0
+     */
+    public String getVersion() {
+        return provider.getVersion();
+    }
+
+    /**
+     * A builder that creates a Native Image debugger with optional displayers.
+     *
+     * @since 1.0
+     */
+    public static final class Builder {
+
+        private final NIDebuggerProvider debuggerProvider;
+
+        Builder(NIDebuggerProvider debuggerProvider) {
+            this.debuggerProvider = debuggerProvider;
+        }
+
+        /**
+         * Displayer of native frames.
+         *
+         * @param frameDisplayer translator of the native frame to it's displayed information
+         * @since 1.0
+         */
+        public Builder frameDisplayer(FrameDisplayer frameDisplayer) {
+            this.debuggerProvider.setFrameDisplayer(frameDisplayer);
+            return this;
+        }
+
+        /**
+         * Displayer of native variables.
+         *
+         * @param variablesDisplayer translator of native variables to displayed variables.
+         * @since 1.0
+         */
+        public Builder variablesDisplayer(VariableDisplayer variablesDisplayer) {
+            this.debuggerProvider.setVariablesDisplayer(variablesDisplayer);
+            return this;
+        }
+
+        /**
+         * Create the debugger instance.
+         * @since 1.0
+         */
+        public NIDebugger build() {
+            return new NIDebugger(debuggerProvider);
+        }
+    }
+}
diff --git a/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIFrame.java b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIFrame.java
new file mode 100644
index 0000000..4ffc48c
--- /dev/null
+++ b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIFrame.java
@@ -0,0 +1,69 @@
+/*
+ * 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.nativeimage.api.debug;
+
+/**
+ * Representation of a native stack frame.
+ *
+ * @since 1.0
+ */
+public interface NIFrame {
+
+    /**
+     * Frame's thread ID.
+     * @since 1.0
+     */
+    String getThreadId();
+
+    /**
+     * Frame's depth level. The top frame has level 0.
+     * @since 1.0
+     */
+    int getLevel();
+
+    /**
+     * Frame's native address.
+     * @since 1.0
+     */
+    String getAddress();
+
+    /**
+     * A short name of the file associated with the frame.
+     * @since 1.0
+     */
+    String getShortFileName();
+
+    /**
+     * A full name of the file associated with the frame.
+     * @since 1.0
+     */
+    String getFullFileName();
+
+    /**
+     * Name of the function associated with the frame.
+     * @since 1.0
+     */
+    String getFunctionName();
+
+    /**
+     * 1-based line of the frame location.
+     * @since 1.0
+     */
+    int getLine();
+}
diff --git a/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NILineBreakpointDescriptor.java b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NILineBreakpointDescriptor.java
new file mode 100644
index 0000000..192be78
--- /dev/null
+++ b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NILineBreakpointDescriptor.java
@@ -0,0 +1,179 @@
+/*
+ * 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.nativeimage.api.debug;
+
+import org.netbeans.api.annotations.common.CheckForNull;
+
+/**
+ * Description of a line native breakpoint.
+ *
+ * @since 1.0
+ */
+public final class NILineBreakpointDescriptor {
+
+    private final String filePath;
+    private final int line;
+    private final boolean enabled;
+    private final String condition;
+    private final boolean hidden;
+
+    private NILineBreakpointDescriptor(String filePath, int line, boolean enabled, String condition, boolean hidden) {
+        this.filePath = filePath;
+        this.line = line;
+        this.enabled = enabled;
+        this.condition = condition;
+        this.hidden = hidden;
+    }
+
+    /**
+     * Create a new line native breakpoint builder.
+     *
+     * @param filePath file path of the breakpoint
+     * @param lineNumber 1-based line number
+     * @since 1.0
+     */
+    public static Builder newBuilder(String filePath, int lineNumber) {
+        return new Builder(filePath, lineNumber);
+    }
+
+    /**
+     * Get path of the file.
+     *
+     * @since 1.0
+     */
+    public String getFilePath() {
+        return filePath;
+    }
+
+    /**
+     * Get 1-based line number.
+     *
+     * @since 1.0
+     */
+    public int getLine() {
+        return line;
+    }
+
+    /**
+     * Check if the breakpoint is to be enabled.
+     *
+     * @since 1.0
+     */
+    public boolean isEnabled() {
+        return enabled;
+    }
+
+    /**
+     * Get the breakpoint condition.
+     *
+     * @return the condition, or <code>null</code> when the breakpoint does not have any condition.
+     * @since 1.0
+     */
+    @CheckForNull
+    public String getCondition() {
+        return condition;
+    }
+
+    /**
+     * Check if the breakpoint is to be hidden (not user-visible).
+     *
+     * @since 1.0
+     */
+    public boolean isHidden() {
+        return hidden;
+    }
+
+    /**
+     * Builder of a line native breakpoint descriptor. The builder is reusable
+     * and the built breakpoint descriptor can be used to update existing breakpoints.
+     *
+     * @since 1.0
+     */
+    public static final class Builder {
+
+        private String filePath;
+        private int line;
+        private boolean enabled = true;
+        private String condition;
+        private boolean hidden = false;
+
+        Builder(String filePath, int lineNumber) {
+            this.filePath = filePath;
+            this.line = lineNumber;
+        }
+
+        /**
+         * Set a file path.
+         *
+         * @since 1.0
+         */
+        public Builder filePath(String filePath) {
+            this.filePath = filePath;
+            return this;
+        }
+
+        /**
+         * Set a 1-based line number.
+         *
+         * @since 1.0
+         */
+        public Builder line(int lineNumber) {
+            this.line = lineNumber;
+            return this;
+        }
+
+        /**
+         * Set a condition.
+         *
+         * @since 1.0
+         */
+        public Builder condition(String condition) {
+            this.condition = condition;
+            return this;
+        }
+
+        /**
+         * Set an enabled state of the breakpoint. The breakpoint is enabled by default.
+         *
+         * @since 1.0
+         */
+        public Builder enabled(boolean enabled) {
+            this.enabled = enabled;
+            return this;
+        }
+
+        /**
+         * Set a hidden state of the breakpoint. Hidden breakpoints are not visible
+         * to users. The breakpoint is not hidden by default.
+         *
+         * @since 1.0
+         */
+        public Builder hidden(boolean hidden) {
+            this.hidden = hidden;
+            return this;
+        }
+
+        /**
+         * Build the line breakpoint descriptor.
+         */
+        public NILineBreakpointDescriptor build() {
+            return new NILineBreakpointDescriptor(filePath, line, enabled, condition, hidden);
+        }
+    }
+}
diff --git a/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIVariable.java b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIVariable.java
new file mode 100644
index 0000000..cd46e7c
--- /dev/null
+++ b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/api/debug/NIVariable.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.nativeimage.api.debug;
+
+/**
+ * Representation of a native variable.
+ * @since 1.0
+ */
+public interface NIVariable {
+
+    /**
+     * Name of the variable.
+     *
+     * @since 1.0
+     */
+    String getName();
+
+    /**
+     * Type of the variable value.
+     *
+     * @since 1.0
+     */
+    String getType();
+
+    /**
+     * String representation of the variable value.
+     *
+     * @since 1.0
+     */
+    String getValue();
+
+    /**
+     * The parent variable, if any. Every child variable has a corresponding parent
+     * variable.
+     *
+     * @return the parent variable, or <code>null</code>.
+     * @since 1.0
+     */
+    NIVariable getParent();
+
+    /**
+     * Number of child variables (properties, or array elements) of this variable.
+     *
+     * @since 1.0
+     */
+    int getNumChildren();
+
+    /**
+     * Get the child variables in the specified index range. Children starting at
+     * <code>from</code> index and up to and excluding <code>to</code> index will
+     * be returned. If <code>from</code> is less than zero, all children are returned.
+     *
+     * @since 1.0
+     */
+    NIVariable[] getChildren(int from, int to);
+
+    /**
+     * Get all variable's children.
+     *
+     * @since 1.0
+     */
+    default NIVariable[] getChildren() {
+        return getChildren(0, Integer.MAX_VALUE);
+    }
+
+    /**
+     * Get the full expression that this variable object represents.
+     *
+     * @since 1.0
+     */
+    String getExpressionPath();
+
+    /**
+     * Get the frame this variable is associated with.
+     *
+     * @return the frame, or <code>null</code>.
+     * @since 1.0
+     */
+    NIFrame getFrame();
+}
diff --git a/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/spi/debug/NIDebuggerProvider.java b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/spi/debug/NIDebuggerProvider.java
new file mode 100644
index 0000000..c2ff425
--- /dev/null
+++ b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/spi/debug/NIDebuggerProvider.java
@@ -0,0 +1,123 @@
+/*
+ * 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.nativeimage.spi.debug;
+
+import java.io.File;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+import org.netbeans.api.debugger.Breakpoint;
+import org.netbeans.api.debugger.DebuggerEngine;
+
+import org.netbeans.api.extexecution.ExecutionDescriptor;
+import org.netbeans.modules.nativeimage.api.debug.NIFrame;
+import org.netbeans.modules.nativeimage.api.debug.NILineBreakpointDescriptor;
+import org.netbeans.modules.nativeimage.api.debug.NIVariable;
+import org.netbeans.modules.nativeimage.spi.debug.filters.FrameDisplayer;
+import org.netbeans.modules.nativeimage.spi.debug.filters.VariableDisplayer;
+
+/**
+ * Provider of the native image debugger.
+ *
+ * @author martin
+ * @since 1.0
+ */
+public interface NIDebuggerProvider {
+
+    /**
+     * Add or change a line breakpoint into the debugger.
+     * A breakpoint is added when the `id` is used for the first time and modified
+     * when breakpoint with that `id` was added already.
+     *
+     * @param id a unique ID of the breakpoint
+     * @param breakpointDescriptor the breakpoint descriptor
+     * @return an instance of the native breakpoint
+     * @since 1.0
+     */
+    Breakpoint addLineBreakpoint(Object id, NILineBreakpointDescriptor breakpointDescriptor);
+
+    /**
+     * Remove breakpoint with the given id.
+     *
+     * @param id the ID of the breakpoint to remove
+     * @since 1.0
+     */
+    void removeBreakpoint(Object id);
+
+    /**
+     * Set a displayer of native frames.
+     *
+     * @param frameDisplayer translator of the native frame to it's displayed information
+     * @since 1.0
+     */
+    void setFrameDisplayer(FrameDisplayer frameDisplayer);
+
+    /**
+     * Set a displayer of native variables.
+     *
+     * @param variablesDisplayer translator of native variables to displayed variables.
+     * @since 1.0
+     */
+    void setVariablesDisplayer(VariableDisplayer variablesDisplayer);
+
+    /**
+     * Start the actual debugging session. Called typically after breakpoints are added.
+     *
+     * @param command a command to run the native image
+     * @param workingDirectory working directory
+     * @param debugger the native debugger command
+     * @param displayName display name of the debugger task
+     * @param executionDescriptor execution descriptor that describes the runtime attributes
+     * @param startedEngine the corresponding DebuggerEngine is passed to this consumer
+     * @param finishedCallback notification of the execution finish
+     * @since 1.0
+     */
+    CompletableFuture<Void> start(List<String> command, File workingDirectory, String debugger, String displayName, ExecutionDescriptor executionDescriptor, Consumer<DebuggerEngine> startedEngine);
+
+    /**
+     * An asynchronous expression evaluation.
+     *
+     * @param expression the expression to evaluate
+     * @param resultName preferred name of the result variable,
+     *                   when <code>null</code> the expression is used as the name
+     * @param frame the frame to evaluate at
+     * @return the completable future with the evaluation result
+     * @since 1.0
+     */
+    CompletableFuture<NIVariable> evaluateAsync(String expression, String resultName, NIFrame frame);
+
+    /**
+     * Read data from memory.
+     *
+     * @param address address where to read the data from
+     * @param offset offset relative to the address where to start reading
+     * @param length number of bytes to read
+     * @return hexadecimal representation of the memory content, or <code>null</code>
+     *         when the read is not successful
+     * @since 1.0
+     */
+    String readMemory(String address, long offset, int length);
+
+    /**
+     * Get version of the underlying native debugger.
+     *
+     * @since 1.0
+     */
+    String getVersion();
+}
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/EvaluateException.java b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/spi/debug/NIDebuggerServiceProvider.java
similarity index 72%
copy from cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/EvaluateException.java
copy to ide/nativeimage.api/src/org/netbeans/modules/nativeimage/spi/debug/NIDebuggerServiceProvider.java
index 0fc9c76..37ec9fe 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/EvaluateException.java
+++ b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/spi/debug/NIDebuggerServiceProvider.java
@@ -16,17 +16,17 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.netbeans.modules.cpplite.debugger;
+package org.netbeans.modules.nativeimage.spi.debug;
 
-public final class EvaluateException extends Exception {
+/**
+ * Provider of the native image debugger.
+ * @since 1.0
+ */
+public interface NIDebuggerServiceProvider {
 
     /**
-     * Constructs an instance of <code>EvaluateException</code> with the
-     * specified detail message.
-     *
-     * @param msg the detail message.
+     * Create a new instance of the debugger provider.
+     * @since 1.0
      */
-    EvaluateException(String msg) {
-        super(msg);
-    }
+    NIDebuggerProvider create();
 }
diff --git a/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/spi/debug/filters/FrameDisplayer.java b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/spi/debug/filters/FrameDisplayer.java
new file mode 100644
index 0000000..91d92cc
--- /dev/null
+++ b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/spi/debug/filters/FrameDisplayer.java
@@ -0,0 +1,166 @@
+/*
+ * 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.nativeimage.spi.debug.filters;
+
+import java.net.URI;
+import java.util.function.Supplier;
+import org.netbeans.api.annotations.common.CheckForNull;
+
+import org.netbeans.modules.nativeimage.api.debug.NIFrame;
+
+/**
+ * Displayer of stack frames. Modifies the way how frames are presented.
+ *
+ * @since 1.0
+ */
+public interface FrameDisplayer {
+
+    /**
+     * Provide display information of a stack frame.
+     *
+     * @param frame the stack frame
+     * @return a display information, or <code>null</code> to skip this frame.
+     * @since 1.0
+     */
+    DisplayedFrame displayed(NIFrame frame);
+
+    /**
+     * Display information of a frame.
+     * @since 1.0
+     */
+    public static final class DisplayedFrame {
+
+        private final String displayName;
+        private final String description;
+        private final int line;
+        private final Supplier<URI> uriSupplier;
+
+        private DisplayedFrame(String displayName, String description, int line, Supplier<URI> uriSupplier) {
+            this.displayName = displayName;
+            this.description = description;
+            this.line = line;
+            this.uriSupplier = uriSupplier;
+        }
+
+        /**
+         * Creates a new builder with a user visible display name of the frame.
+         *
+         * @since 1.0
+         */
+        public static Builder newBuilder(String displayName) {
+            return new Builder(displayName);
+        }
+
+        /**
+         * Get a display name of the frame.
+         *
+         * @since 1.0
+         */
+        public String getDisplayName() {
+            return displayName;
+        }
+
+        /**
+         * Get a description of the frame.
+         *
+         * @since 1.0
+         */
+        public String getDescription() {
+            return description;
+        }
+
+        /**
+         * Get a 1-based line number of the frame.
+         *
+         * @since 1.0
+         */
+        public int getLine() {
+            return line;
+        }
+
+        /**
+         * Get URI of the source file associated with the frame.
+         *
+         * @return the URI, or <code>null</code> when unknown.
+         * @since 1.0
+         */
+        @CheckForNull
+        public URI getSourceURI() {
+            if (uriSupplier != null) {
+                return uriSupplier.get();
+            } else {
+                return null;
+            }
+        }
+
+        /**
+         * Builder of the {@link DisplayedFrame}.
+         * @since 1.0
+         */
+        public static final class Builder {
+
+            private final String displayName;
+            private String description;
+            private int line;
+            private Supplier<URI> uriSupplier;
+
+            Builder(String displayName) {
+                this.displayName = displayName;
+            }
+
+            /**
+             * Set frame description.
+             *
+             * @since 1.0
+             */
+            public Builder description(String description) {
+                this.description = description;
+                return this;
+            }
+
+            /**
+             * 1-based line number information of the frame location.
+             *
+             * @since 1.0
+             */
+            public Builder line(int lineNumber) {
+                this.line = lineNumber;
+                return this;
+            }
+
+            /**
+             * URI of the source file associated with the frame.
+             * @since 1.0
+             */
+            public Builder sourceURISupplier(Supplier<URI> uriSupplier) {
+                this.uriSupplier = uriSupplier;
+                return this;
+            }
+
+            /**
+             * Build the {@link DisplayedFrame}.
+             *
+             * @since 1.0
+             */
+            public DisplayedFrame build() {
+                return new DisplayedFrame(displayName, description, line, uriSupplier);
+            }
+        }
+    }
+}
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/EvaluateException.java b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/spi/debug/filters/VariableDisplayer.java
similarity index 64%
copy from cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/EvaluateException.java
copy to ide/nativeimage.api/src/org/netbeans/modules/nativeimage/spi/debug/filters/VariableDisplayer.java
index 0fc9c76..9d6cfa1 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/EvaluateException.java
+++ b/ide/nativeimage.api/src/org/netbeans/modules/nativeimage/spi/debug/filters/VariableDisplayer.java
@@ -16,17 +16,22 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.netbeans.modules.cpplite.debugger;
+package org.netbeans.modules.nativeimage.spi.debug.filters;
 
-public final class EvaluateException extends Exception {
+import org.netbeans.modules.nativeimage.api.debug.NIVariable;
+
+/**
+ * Displayer of variables. Modifies the way how variables are presented.
+ *
+ * @since 1.0
+ */
+public interface VariableDisplayer {
 
     /**
-     * Constructs an instance of <code>EvaluateException</code> with the
-     * specified detail message.
-     *
-     * @param msg the detail message.
+     * Translate a list of original native variables into a list of variables
+     * presented to the debugger user.
+     * @since 1.0
      */
-    EvaluateException(String msg) {
-        super(msg);
-    }
+    NIVariable[] displayed(NIVariable... variables);
+
 }
diff --git a/ide/nativeimage.api/test/unit/src/org/netbeans/modules/nativeimage/debug/NIDebuggerServiceTest.java b/ide/nativeimage.api/test/unit/src/org/netbeans/modules/nativeimage/debug/NIDebuggerServiceTest.java
new file mode 100644
index 0000000..f7ad3dd
--- /dev/null
+++ b/ide/nativeimage.api/test/unit/src/org/netbeans/modules/nativeimage/debug/NIDebuggerServiceTest.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.nativeimage.debug;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.concurrent.CompletableFuture;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+
+import org.netbeans.modules.nativeimage.api.debug.NIDebugger;
+import org.netbeans.modules.nativeimage.api.debug.NILineBreakpointDescriptor;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.Lookups;
+import org.openide.util.lookup.ProxyLookup;
+
+public class NIDebuggerServiceTest {
+
+    public NIDebuggerServiceTest() {
+    }
+
+    private static Lookup getTestLookup(String checkStartParams) {
+        Lookup launchCtx = new ProxyLookup(
+                        Lookups.fixed(new TestNIDebuggerServiceProvider(checkStartParams)),
+                        Lookup.getDefault()
+                );
+        return launchCtx;
+    }
+
+    @Test
+    public void testFindServiceProvider() {
+        Lookups.executeWith(getTestLookup(""), () -> {
+            NIDebugger debugger = NIDebugger.newBuilder().build();
+            assertNotNull("Test NI debugger service is not available.", debugger);
+        });
+    }
+
+    @Test
+    public void testDebuggerProvider() {
+        String checkStartParams = "[CMD1, CMD2]WDMIDdisplayNamenull{ID1=TEST ID1filePath110truecondition1false}nullnull";
+        Lookups.executeWith(getTestLookup(checkStartParams), () -> {
+            NIDebugger debugger = NIDebugger.newBuilder().build();
+            debugger.addLineBreakpoint("ID1", NILineBreakpointDescriptor.newBuilder("filePath1", 10).enabled(true).condition("condition1").hidden(false).build());
+            debugger.addLineBreakpoint("ID2", NILineBreakpointDescriptor.newBuilder("filePath2", 20).enabled(false).condition("condition2").hidden(true).build());
+            debugger.removeBreakpoint("ID2");
+            CompletableFuture<Void> completed = debugger.start(Arrays.asList("CMD1", "CMD2"), new File("WD"), "MID", "displayName", null, engine -> {});
+            assertTrue(completed.isDone());
+        });
+    }
+}
diff --git a/ide/nativeimage.api/test/unit/src/org/netbeans/modules/nativeimage/debug/TestNIDebuggerProvider.java b/ide/nativeimage.api/test/unit/src/org/netbeans/modules/nativeimage/debug/TestNIDebuggerProvider.java
new file mode 100644
index 0000000..309b859
--- /dev/null
+++ b/ide/nativeimage.api/test/unit/src/org/netbeans/modules/nativeimage/debug/TestNIDebuggerProvider.java
@@ -0,0 +1,103 @@
+/*
+ * 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.nativeimage.debug;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+import static org.junit.Assert.assertEquals;
+import org.netbeans.api.debugger.Breakpoint;
+import org.netbeans.api.debugger.DebuggerEngine;
+
+import org.netbeans.api.extexecution.ExecutionDescriptor;
+import org.netbeans.modules.nativeimage.api.debug.NIFrame;
+import org.netbeans.modules.nativeimage.api.debug.NILineBreakpointDescriptor;
+import org.netbeans.modules.nativeimage.api.debug.NIVariable;
+import org.netbeans.modules.nativeimage.spi.debug.NIDebuggerProvider;
+import org.netbeans.modules.nativeimage.spi.debug.filters.FrameDisplayer;
+import org.netbeans.modules.nativeimage.spi.debug.filters.VariableDisplayer;
+
+public class TestNIDebuggerProvider implements NIDebuggerProvider {
+
+    private final Map<Object, String> breakpoints = new HashMap<>();
+    private final String checkStartParams;
+    private FrameDisplayer frameDisplayer;
+    private VariableDisplayer variablesDisplayer;
+
+    public TestNIDebuggerProvider(String checkStartParams) {
+        this.checkStartParams = checkStartParams;
+    }
+
+    @Override
+    public Breakpoint addLineBreakpoint(Object id, NILineBreakpointDescriptor bd) {
+        String testBP = id + bd.getFilePath() + bd.getLine() + bd.isEnabled() + bd.getCondition() + bd.isHidden();
+        breakpoints.put(id, testBP);
+        return new Breakpoint() {
+            @Override
+            public boolean isEnabled() {
+                return true;
+            }
+            @Override
+            public void disable() {}
+            @Override
+            public void enable() {}
+        };
+    }
+
+    @Override
+    public void removeBreakpoint(Object id) {
+        breakpoints.remove(id);
+    }
+
+    @Override
+    public void setFrameDisplayer(FrameDisplayer frameDisplayer) {
+        this.frameDisplayer = frameDisplayer;
+    }
+
+    @Override
+    public void setVariablesDisplayer(VariableDisplayer variablesDisplayer) {
+        this.variablesDisplayer = variablesDisplayer;
+    }
+
+    @Override
+    public CompletableFuture<Void> start(List<String> command, File workingDirectory, String debugger, String displayName, ExecutionDescriptor executionDescriptor, Consumer<DebuggerEngine> startedEngine) {
+        assertEquals(checkStartParams, command.toString() + workingDirectory + debugger + displayName + executionDescriptor + breakpoints.toString() + frameDisplayer + variablesDisplayer);
+        startedEngine.accept(null);
+        return CompletableFuture.completedFuture(null);
+    }
+
+    @Override
+    public CompletableFuture<NIVariable> evaluateAsync(String expression, String resultName, NIFrame frame) {
+        return CompletableFuture.completedFuture(null);
+    }
+
+    @Override
+    public String readMemory(String address, long offset, int length) {
+        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+    }
+
+    @Override
+    public String getVersion() {
+        return "Test_NI";
+    }
+
+}
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/EvaluateException.java b/ide/nativeimage.api/test/unit/src/org/netbeans/modules/nativeimage/debug/TestNIDebuggerServiceProvider.java
similarity index 59%
copy from cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/EvaluateException.java
copy to ide/nativeimage.api/test/unit/src/org/netbeans/modules/nativeimage/debug/TestNIDebuggerServiceProvider.java
index 0fc9c76..7a16f19 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/EvaluateException.java
+++ b/ide/nativeimage.api/test/unit/src/org/netbeans/modules/nativeimage/debug/TestNIDebuggerServiceProvider.java
@@ -16,17 +16,22 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.netbeans.modules.cpplite.debugger;
+package org.netbeans.modules.nativeimage.debug;
 
-public final class EvaluateException extends Exception {
+import org.netbeans.modules.nativeimage.spi.debug.NIDebuggerProvider;
+import org.netbeans.modules.nativeimage.spi.debug.NIDebuggerServiceProvider;
 
-    /**
-     * Constructs an instance of <code>EvaluateException</code> with the
-     * specified detail message.
-     *
-     * @param msg the detail message.
-     */
-    EvaluateException(String msg) {
-        super(msg);
+public class TestNIDebuggerServiceProvider implements NIDebuggerServiceProvider {
+
+    private final String checkStartParams;
+
+    public TestNIDebuggerServiceProvider(String checkStartParams) {
+        this.checkStartParams = checkStartParams;
     }
+
+    @Override
+    public NIDebuggerProvider create() {
+        return new TestNIDebuggerProvider(checkStartParams);
+    }
+
 }
diff --git a/java/java.kit/nbproject/project.xml b/java/java.kit/nbproject/project.xml
index 51b43fc..607df4c 100644
--- a/java/java.kit/nbproject/project.xml
+++ b/java/java.kit/nbproject/project.xml
@@ -167,6 +167,13 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
+                    <code-name-base>org.netbeans.modules.java.nativeimage.debugger</code-name-base>
+                    <run-dependency>
+                        <release-version>0</release-version>
+                        <specification-version>0.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
                     <code-name-base>org.netbeans.modules.java.navigation</code-name-base>
                     <run-dependency>
                         <release-version>1</release-version>
diff --git a/java/java.lsp.server/nbcode/nbproject/platform.properties b/java/java.lsp.server/nbcode/nbproject/platform.properties
index a4903f4..9e0f111 100644
--- a/java/java.lsp.server/nbcode/nbproject/platform.properties
+++ b/java/java.lsp.server/nbcode/nbproject/platform.properties
@@ -17,6 +17,7 @@
 
 branding.token=nbcode
 cluster.path=\
+    ${nbplatform.active.dir}/cpplite:\
     ${nbplatform.active.dir}/enterprise:\
     ${nbplatform.active.dir}/extide:\
     ${nbplatform.active.dir}/ide:\
@@ -30,8 +31,6 @@ disabled.modules=\
     bcprov,\
     com.google.guava,\
     com.googlecode.javaewah.JavaEWAH,\
-    com.jcraft.jsch,\
-    com.jcraft.jzlib,\
     libs.c.kohlschutter.junixsocket,\
     org.apache.commons.httpclient,\
     org.apache.commons.lang,\
@@ -110,6 +109,9 @@ disabled.modules=\
     org.netbeans.modules.cordova.platforms,\
     org.netbeans.modules.cordova.platforms.android,\
     org.netbeans.modules.core.kit,\
+    org.netbeans.modules.cpplite.editor,\
+    org.netbeans.modules.cpplite.kit,\
+    org.netbeans.modules.cpplite.project,\
     org.netbeans.modules.css.editor,\
     org.netbeans.modules.css.model,\
     org.netbeans.modules.css.prep,\
@@ -132,7 +134,6 @@ disabled.modules=\
     org.netbeans.modules.debugger.jpda.kit,\
     org.netbeans.modules.debugger.jpda.visual,\
     org.netbeans.modules.derby,\
-    org.netbeans.modules.dlight.nativeexecution,\
     org.netbeans.modules.dlight.nativeexecution.nb,\
     org.netbeans.modules.dlight.terminal,\
     org.netbeans.modules.docker.api,\
@@ -300,7 +301,6 @@ disabled.modules=\
     org.netbeans.modules.team.commons,\
     org.netbeans.modules.team.ide,\
     org.netbeans.modules.templates,\
-    org.netbeans.modules.terminal,\
     org.netbeans.modules.terminal.nb,\
     org.netbeans.modules.testng.ant,\
     org.netbeans.modules.testng.maven,\
diff --git a/java/java.lsp.server/nbproject/project.xml b/java/java.lsp.server/nbproject/project.xml
index d78ddd8..eb90897 100644
--- a/java/java.lsp.server/nbproject/project.xml
+++ b/java/java.lsp.server/nbproject/project.xml
@@ -321,6 +321,24 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
+                    <code-name-base>org.netbeans.modules.nativeimage.api</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>0</release-version>
+                        <specification-version>0.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.java.nativeimage.debugger</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>0</release-version>
+                        <specification-version>0.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
                     <code-name-base>org.netbeans.modules.parsing.api</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
diff --git a/java/java.lsp.server/script/etc/nbcode.clusters b/java/java.lsp.server/script/etc/nbcode.clusters
index 2151943..9a5a263 100644
--- a/java/java.lsp.server/script/etc/nbcode.clusters
+++ b/java/java.lsp.server/script/etc/nbcode.clusters
@@ -3,6 +3,7 @@ ide
 extide
 webcommon
 java
+cpplite
 extra
 enterprise
 nbcode
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/NbProtocolServer.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/NbProtocolServer.java
index 6b274d2..4541ed3 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/NbProtocolServer.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/NbProtocolServer.java
@@ -29,6 +29,8 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.commons.lang3.StringUtils;
 import org.eclipse.lsp4j.debug.Capabilities;
@@ -69,6 +71,7 @@ import org.eclipse.lsp4j.debug.services.IDebugProtocolServer;
 import org.eclipse.lsp4j.jsonrpc.messages.ResponseErrorCode;
 import org.netbeans.api.debugger.ActionsManager;
 import org.netbeans.api.debugger.DebuggerManager;
+import org.netbeans.api.debugger.Session;
 import org.netbeans.api.debugger.jpda.InvalidExpressionException;
 import org.netbeans.api.debugger.jpda.JPDADebugger;
 import org.netbeans.api.debugger.jpda.ObjectVariable;
@@ -80,6 +83,9 @@ import org.netbeans.modules.java.lsp.server.debugging.launch.NbLaunchRequestHand
 import org.netbeans.modules.java.lsp.server.debugging.breakpoints.NbBreakpointsRequestHandler;
 import org.netbeans.modules.java.lsp.server.debugging.variables.NbVariablesRequestHandler;
 import org.netbeans.modules.java.lsp.server.debugging.utils.ErrorUtilities;
+import org.netbeans.modules.nativeimage.api.debug.EvaluateException;
+import org.netbeans.modules.nativeimage.api.debug.NIDebugger;
+import org.netbeans.modules.nativeimage.api.debug.NIVariable;
 import org.netbeans.spi.debugger.ui.DebuggingView.DVFrame;
 import org.netbeans.spi.debugger.ui.DebuggingView.DVThread;
 
@@ -187,8 +193,8 @@ public final class NbProtocolServer implements IDebugProtocolServer {
             }
             response.setAllThreadsContinued(false);
         } else {
-            JPDADebugger debugger = context.getDebugSession().getDebugger();
-            debugger.getSession().getCurrentEngine().getActionsManager().doAction("continue");
+            Session session = context.getDebugSession().getSession();
+            session.getCurrentEngine().getActionsManager().doAction("continue");
             context.getThreadsProvider().getThreadObjects().cleanAll();
             response.setAllThreadsContinued(true);
         }
@@ -249,8 +255,8 @@ public final class NbProtocolServer implements IDebugProtocolServer {
                 ev = null;
             }
         } else {
-            JPDADebugger debugger = context.getDebugSession().getDebugger();
-            debugger.getSession().getCurrentEngine().getActionsManager().doAction("pause");
+            Session session = context.getDebugSession().getSession();
+            session.getCurrentEngine().getActionsManager().doAction("pause");
             ev = new StoppedEventArguments();
             ev.setReason("pause");
             ev.setThreadId(0);
@@ -417,48 +423,78 @@ public final class NbProtocolServer implements IDebugProtocolServer {
             stackFrame.getDVFrame().makeCurrent(); // The evaluation is always performed with respect to the current frame
             DVThread dvThread = stackFrame.getDVFrame().getThread();
             int threadId = context.getThreadsProvider().getId(dvThread);
-            JPDADebugger debugger = context.getDebugSession().getDebugger();
-            Variable variable;
-            try {
-                variable = debugger.evaluate(expression);
-            } catch (InvalidExpressionException ex) {
-                throw ErrorUtilities.createResponseErrorException(
-                    ex.getLocalizedMessage(),
-                    ResponseErrorCode.ParseError);
-            }
             EvaluateResponse response = new EvaluateResponse();
-            TruffleVariable truffleVariable = TruffleVariable.get(variable);
-            if (truffleVariable != null) {
-                int referenceId = context.getThreadsProvider().getThreadObjects().addObject(threadId, truffleVariable);
-                response.setResult(truffleVariable.getDisplayValue());
-                response.setVariablesReference(referenceId);
-                response.setType(truffleVariable.getType());
-                response.setIndexedVariables(truffleVariable.isLeaf() ? 0 : truffleVariable.getChildren().length);
+            JPDADebugger debugger = context.getDebugSession().getJPDADebugger();
+            if (debugger != null) {
+                evaluateJPDA(debugger, expression, threadId, response);
             } else {
-                if (variable instanceof ObjectVariable) {
-                    int referenceId = context.getThreadsProvider().getThreadObjects().addObject(threadId, variable);
-                    int indexedVariables = ((ObjectVariable) variable).getFieldsCount();
-                    String toString;
-                    try {
-                        toString = ((ObjectVariable) variable).getToStringValue();
-                    } catch (InvalidExpressionException ex) {
-                        toString = variable.getValue();
-                    }
-                    response.setResult(toString);
-                    response.setVariablesReference(referenceId);
-                    response.setType(variable.getType());
-                    response.setIndexedVariables(Math.max(indexedVariables, 0));
-                } else {
-                    response.setResult(variable.getValue());
-                    response.setVariablesReference(0);
-                    response.setType(variable.getType());
-                    response.setIndexedVariables(0);
-                }
+                NIDebugger niDebugger = context.getDebugSession().getNIDebugger();
+                evaluateNative(niDebugger, expression, threadId, response);
             }
             return response;
         });
     }
 
+    private void evaluateJPDA(JPDADebugger debugger, String expression, int threadId, EvaluateResponse response) {
+        Variable variable;
+        try {
+            variable = debugger.evaluate(expression);
+        } catch (InvalidExpressionException ex) {
+            throw ErrorUtilities.createResponseErrorException(
+                ex.getLocalizedMessage(),
+                ResponseErrorCode.ParseError);
+        }
+        TruffleVariable truffleVariable = TruffleVariable.get(variable);
+        if (truffleVariable != null) {
+            int referenceId = context.getThreadsProvider().getThreadObjects().addObject(threadId, truffleVariable);
+            response.setResult(truffleVariable.getDisplayValue());
+            response.setVariablesReference(referenceId);
+            response.setType(truffleVariable.getType());
+            response.setIndexedVariables(truffleVariable.isLeaf() ? 0 : truffleVariable.getChildren().length);
+        } else {
+            if (variable instanceof ObjectVariable) {
+                int referenceId = context.getThreadsProvider().getThreadObjects().addObject(threadId, variable);
+                int indexedVariables = ((ObjectVariable) variable).getFieldsCount();
+                String toString;
+                try {
+                    toString = ((ObjectVariable) variable).getToStringValue();
+                } catch (InvalidExpressionException ex) {
+                    toString = variable.getValue();
+                }
+                response.setResult(toString);
+                response.setVariablesReference(referenceId);
+                response.setType(variable.getType());
+                response.setIndexedVariables(Math.max(indexedVariables, 0));
+            } else {
+                response.setResult(variable.getValue());
+                //response.setVariablesReference(0);
+                response.setType(variable.getType());
+                //response.setIndexedVariables(0);
+            }
+        }
+    }
+
+    private void evaluateNative(NIDebugger niDebugger, String expression, int threadId, EvaluateResponse response) {
+        try {
+            NIVariable variable = niDebugger.evaluate(expression, null, null);
+            int numChildren = variable.getNumChildren();
+            if (numChildren > 0) {
+                int referenceId = context.getThreadsProvider().getThreadObjects().addObject(threadId, variable);
+                response.setResult(variable.getValue());
+                response.setVariablesReference(referenceId);
+                response.setType(variable.getType());
+                response.setIndexedVariables(numChildren);
+            } else {
+                response.setResult(variable.getValue());
+                response.setType(variable.getType());
+            }
+        } catch (EvaluateException ex) {
+            throw ErrorUtilities.createResponseErrorException(
+                ex.getLocalizedMessage(),
+                ResponseErrorCode.ParseError);
+        }
+    }
+
     @Override
     public CompletableFuture<ExceptionInfoResponse> exceptionInfo(ExceptionInfoArguments args) {
         CompletableFuture<ExceptionInfoResponse> future = new CompletableFuture<>();
@@ -466,7 +502,6 @@ public final class NbProtocolServer implements IDebugProtocolServer {
         if (exceptionVariable == null) {
             ErrorUtilities.completeExceptionally(future, "No exception exists in thread " + args.getThreadId(), ResponseErrorCode.InvalidParams);
         } else {
-            JPDADebugger debugger = context.getDebugSession().getDebugger();
             Throwable exception = (Throwable) exceptionVariable.createMirrorObject();
             String typeName = exception.getLocalizedMessage(); // TODO
             String exceptionToString = exception.toString();
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/NbThreads.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/NbThreads.java
index 4e7e439..96c5f79 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/NbThreads.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/NbThreads.java
@@ -55,28 +55,27 @@ public final class NbThreads {
             @Override
             public void sessionAdded(Session session) {
                 DebuggerManager.getDebuggerManager().removeDebuggerListener(DebuggerManager.PROP_SESSIONS, this);
-                JPDADebugger debugger = session.lookupFirst(null, JPDADebugger.class);
-                initThreads(context, debugger);
+                initThreads(context, session);
             }
         });
     }
 
-    private void initThreads(DebugAdapterContext context, JPDADebugger debugger) {
-        DebuggerEngine engine = debugger.getSession().getCurrentEngine();
+    private void initThreads(DebugAdapterContext context, Session session) {
+        DebuggerEngine engine = session.getCurrentEngine();
         if (engine == null) {
-            debugger.getSession().addPropertyChangeListener(Session.PROP_CURRENT_LANGUAGE, new PropertyChangeListener() {
+            session.addPropertyChangeListener(Session.PROP_CURRENT_LANGUAGE, new PropertyChangeListener() {
                 @Override
                 public void propertyChange(PropertyChangeEvent evt) {
-                    DebuggerEngine currentEngine = debugger.getSession().getCurrentEngine();
+                    DebuggerEngine currentEngine = session.getCurrentEngine();
                     if (currentEngine != null) {
-                        debugger.getSession().removePropertyChangeListener(Session.PROP_CURRENT_LANGUAGE, this);
+                        session.removePropertyChangeListener(Session.PROP_CURRENT_LANGUAGE, this);
                         if (!initialized.getAndSet(true)) {
                             initThreads(context, currentEngine);
                         }
                     }
                 }
             });
-            engine = debugger.getSession().getCurrentEngine();
+            engine = session.getCurrentEngine();
         }
         if (engine != null && !initialized.getAndSet(true)) {
             initThreads(context, engine);
@@ -209,7 +208,11 @@ public final class NbThreads {
 
     private JPDAThread getJPDAThread(DVThread dvThread) {
         // JPDA implementation implements Supplier.
-        return ((Supplier<JPDAThread>) dvThread).get();
+        if (dvThread instanceof Supplier) {
+            return ((Supplier<JPDAThread>) dvThread).get();
+        } else {
+            return null;
+        }
     }
 
     public void visitThreads(BiConsumer<Integer, DVThread> threadsConsumer) {
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbDebugSession.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbDebugSession.java
index 1167740..e18052c 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbDebugSession.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbDebugSession.java
@@ -18,7 +18,9 @@
  */
 package org.netbeans.modules.java.lsp.server.debugging.launch;
 
+import org.netbeans.api.debugger.Session;
 import org.netbeans.api.debugger.jpda.JPDADebugger;
+import org.netbeans.modules.nativeimage.api.debug.NIDebugger;
 
 /**
  *
@@ -26,14 +28,27 @@ import org.netbeans.api.debugger.jpda.JPDADebugger;
  */
 public final class NbDebugSession {
 
-    private final JPDADebugger debugger;
+    private final Session session;
+    private volatile NIDebugger niDebugger;
 
-    NbDebugSession(JPDADebugger debugger) {
-        this.debugger = debugger;
+    NbDebugSession(Session session) {
+        this.session = session;
     }
 
-    public JPDADebugger getDebugger() {
-        return debugger;
+    public Session getSession() {
+        return session;
+    }
+
+    public JPDADebugger getJPDADebugger() {
+        return session.lookupFirst(null, JPDADebugger.class);
+    }
+
+    public NIDebugger getNIDebugger() {
+        return niDebugger;
+    }
+
+    void setNIDebugger(NIDebugger niDebugger) {
+        this.niDebugger = niDebugger;
     }
 
     public void detach() {
@@ -41,7 +56,7 @@ public final class NbDebugSession {
     }
 
     public void terminate() {
-        debugger.getSession().kill();
+        session.kill();
     }
 
     public void setExceptionBreakpoints(boolean notifyCaught, boolean notifyUncaught) {
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbLaunchDelegate.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbLaunchDelegate.java
index 789a072..a254307 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbLaunchDelegate.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbLaunchDelegate.java
@@ -20,6 +20,8 @@ package org.netbeans.modules.java.lsp.server.debugging.launch;
 
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -27,6 +29,7 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Consumer;
 
 import org.eclipse.lsp4j.jsonrpc.ResponseErrorException;
@@ -40,6 +43,8 @@ import org.netbeans.api.debugger.DebuggerManagerAdapter;
 import org.netbeans.api.debugger.Session;
 import org.netbeans.api.debugger.jpda.JPDADebugger;
 import org.netbeans.api.extexecution.base.ExplicitProcessParameters;
+import org.netbeans.api.extexecution.ExecutionDescriptor;
+import org.netbeans.api.extexecution.ExecutionService;
 import org.netbeans.api.java.classpath.ClassPath;
 import org.netbeans.api.java.queries.UnitTestForSourceQuery;
 import org.netbeans.api.project.FileOwnerQuery;
@@ -48,10 +53,13 @@ import org.netbeans.api.project.ProjectUtils;
 import org.netbeans.modules.java.lsp.server.Utils;
 import org.netbeans.modules.java.lsp.server.debugging.DebugAdapterContext;
 import org.netbeans.modules.java.lsp.server.debugging.NbSourceProvider;
+import org.netbeans.modules.java.lsp.server.debugging.utils.ErrorUtilities;
 import org.netbeans.modules.java.lsp.server.progress.OperationContext;
 import org.netbeans.modules.java.lsp.server.progress.ProgressOperationEvent;
 import org.netbeans.modules.java.lsp.server.progress.ProgressOperationListener;
 import org.netbeans.modules.java.lsp.server.progress.TestProgressHandler;
+import org.netbeans.modules.java.nativeimage.debugger.api.NIDebugRunner;
+import org.netbeans.modules.nativeimage.api.debug.NIDebugger;
 import org.netbeans.spi.project.ActionProgress;
 import org.netbeans.spi.project.ActionProvider;
 import org.netbeans.spi.project.SingleMethod;
@@ -75,7 +83,7 @@ public abstract class NbLaunchDelegate {
         // no op.
     }
 
-    public final CompletableFuture<Void> nbLaunch(FileObject toRun, String method, Map<String, Object> launchArguments, DebugAdapterContext context, boolean debug, boolean testRun, Consumer<NbProcessConsole.ConsoleMessage> consoleMessages) {
+    public final CompletableFuture<Void> nbLaunch(FileObject toRun, File nativeImageFile, String method, Map<String, Object> launchArguments, DebugAdapterContext context, boolean debug, boolean testRun, Consumer<NbProcessConsole.ConsoleMessage> consoleMessages) {
         CompletableFuture<Void> launchFuture = new CompletableFuture<>();
         NbProcessConsole ioContext = new NbProcessConsole(consoleMessages);
         SingleMethod singleMethod;
@@ -84,88 +92,161 @@ public abstract class NbLaunchDelegate {
         } else {
             singleMethod = null;
         }
-        CompletableFuture<Pair<ActionProvider, String>> commandFuture = findTargetWithPossibleRebuild(toRun, singleMethod, debug, testRun, ioContext);
-        commandFuture.thenAccept((providerAndCommand) -> {
-            if (debug) {
-                DebuggerManager.getDebuggerManager().addDebuggerListener(new DebuggerManagerAdapter() {
-                    @Override
-                    public void sessionAdded(Session session) {
-                        JPDADebugger debugger = session.lookupFirst(null, JPDADebugger.class);
-                        if (debugger != null) {
-                            DebuggerManager.getDebuggerManager().removeDebuggerListener(this);
-                            Map properties = session.lookupFirst(null, Map.class);
-                            NbSourceProvider sourceProvider = context.getSourceProvider();
-                            sourceProvider.setSourcePath(properties != null ? (ClassPath) properties.getOrDefault("sourcepath", ClassPath.EMPTY) : ClassPath.EMPTY);
-                            debugger.addPropertyChangeListener(JPDADebugger.PROP_STATE, new PropertyChangeListener() {
-                                @Override
-                                public void propertyChange(PropertyChangeEvent evt) {
-                                    int newState = (int) evt.getNewValue();
-                                    if (newState == JPDADebugger.STATE_RUNNING) {
-                                        debugger.removePropertyChangeListener(JPDADebugger.PROP_STATE, this);
-                                        NbDebugSession debugSession = new NbDebugSession(debugger);
-                                        context.setDebugSession(debugSession);
-                                        launchFuture.complete(null);
-                                        context.getConfigurationSemaphore().waitForConfigurationDone();
+        ActionProgress progress = new ActionProgress() {
+            @Override
+            protected void started() {
+            }
+
+            @Override
+            public void finished(boolean success) {
+                ioContext.stop();
+                notifyFinished(context, success);
+            }
+        };
+        if (toRun != null) {
+            CompletableFuture<Pair<ActionProvider, String>> commandFuture = findTargetWithPossibleRebuild(toRun, singleMethod, debug, testRun, ioContext);
+            commandFuture.thenAccept((providerAndCommand) -> {
+                if (debug) {
+                    DebuggerManager.getDebuggerManager().addDebuggerListener(new DebuggerManagerAdapter() {
+                        @Override
+                        public void sessionAdded(Session session) {
+                            JPDADebugger debugger = session.lookupFirst(null, JPDADebugger.class);
+                            if (debugger != null) {
+                                DebuggerManager.getDebuggerManager().removeDebuggerListener(this);
+                                Map properties = session.lookupFirst(null, Map.class);
+                                NbSourceProvider sourceProvider = context.getSourceProvider();
+                                sourceProvider.setSourcePath(properties != null ? (ClassPath) properties.getOrDefault("sourcepath", ClassPath.EMPTY) : ClassPath.EMPTY);
+                                debugger.addPropertyChangeListener(JPDADebugger.PROP_STATE, new PropertyChangeListener() {
+                                    @Override
+                                    public void propertyChange(PropertyChangeEvent evt) {
+                                        int newState = (int) evt.getNewValue();
+                                        if (newState == JPDADebugger.STATE_RUNNING) {
+                                            debugger.removePropertyChangeListener(JPDADebugger.PROP_STATE, this);
+                                            NbDebugSession debugSession = new NbDebugSession(session);
+                                            context.setDebugSession(debugSession);
+                                            launchFuture.complete(null);
+                                            context.getConfigurationSemaphore().waitForConfigurationDone();
+                                        }
                                     }
-                                }
-                            });
+                                });
+                            }
                         }
+                    });
+                } else {
+                    launchFuture.complete(null);
+                }
+                List<String> args = argsToStringList(launchArguments.get("args"));
+                List<String> vmArgs = argsToStringList(launchArguments.get("vmArgs"));
+                ExplicitProcessParameters params = ExplicitProcessParameters.empty();
+                if (!(args.isEmpty() && vmArgs.isEmpty())) {
+                    ExplicitProcessParameters.Builder bld = ExplicitProcessParameters.builder();
+                    bld.launcherArgs(vmArgs);
+                    bld.args(args);
+                    bld.replaceArgs(false);
+                    params = bld.build();
+                }
+                OperationContext ctx = OperationContext.find(Lookup.getDefault());
+                ctx.addProgressOperationListener(null, new ProgressOperationListener() {
+                    @Override
+                    public void progressHandleCreated(ProgressOperationEvent e) {
+                        context.setProcessExecutorHandle(e.getProgressHandle());
                     }
                 });
-            } else {
-                launchFuture.complete(null);
-            }
-            ActionProgress progress = new ActionProgress() {
-                @Override
-                protected void started() {
-                }
+                TestProgressHandler testProgressHandler = ctx.getClient().getNbCodeCapabilities().hasTestResultsSupport() ? new TestProgressHandler(ctx.getClient(), context.getClient(), Utils.toUri(toRun)) : null;
+                Lookup launchCtx = new ProxyLookup(
+                        testProgressHandler != null ? Lookups.fixed(toRun, ioContext, progress, testProgressHandler) : Lookups.fixed(toRun, ioContext, progress),
+                        Lookup.getDefault()
+                );
 
-                @Override
-                public void finished(boolean success) {
-                    ioContext.stop();
-                    notifyFinished(context, success);
-                }
-            };
-            List<String> args = argsToStringList(launchArguments.get("args"));
-            List<String> vmArgs = argsToStringList(launchArguments.get("vmArgs"));
-            ExplicitProcessParameters params = ExplicitProcessParameters.empty();
-            if (!(args.isEmpty() && vmArgs.isEmpty())) {
-                ExplicitProcessParameters.Builder bld = ExplicitProcessParameters.builder();
-                bld.launcherArgs(vmArgs);
-                bld.args(args);
-                bld.replaceArgs(false);
-                params = bld.build();
-            }
-            OperationContext ctx = OperationContext.find(Lookup.getDefault());
-            ctx.addProgressOperationListener(null, new ProgressOperationListener() {
-                @Override
-                public void progressHandleCreated(ProgressOperationEvent e) {
-                    context.setProcessExecutorHandle(e.getProgressHandle());
+                Lookup lookup;
+                if (singleMethod != null) {
+                    lookup = Lookups.fixed(toRun, singleMethod, params, ioContext, progress);
+                } else {
+                    lookup = Lookups.fixed(toRun, ioContext, params, progress);
                 }
+                Lookups.executeWith(launchCtx, () -> {
+                    providerAndCommand.first().invokeAction(providerAndCommand.second(), lookup);
+
+                });
+            }).exceptionally((t) -> {
+                launchFuture.completeExceptionally(t);
+                return null;
             });
-            TestProgressHandler testProgressHandler = ctx.getClient().getNbCodeCapabilities().hasTestResultsSupport() ? new TestProgressHandler(ctx.getClient(), context.getClient(), Utils.toUri(toRun)) : null;
+        } else {
+            ExecutionDescriptor executionDescriptor = new ExecutionDescriptor()
+                    .showProgress(true)
+                    .showSuspended(true)
+                    .frontWindowOnError(true)
+                    .controllable(true);
             Lookup launchCtx = new ProxyLookup(
-                    testProgressHandler != null ? Lookups.fixed(toRun, ioContext, progress, testProgressHandler) : Lookups.fixed(toRun, ioContext, progress),
+                    Lookups.fixed(ioContext, progress),
                     Lookup.getDefault()
             );
+            List<String> args = argsToStringList(launchArguments.get("args"));
+            Lookups.executeWith(launchCtx, () -> {
+                if (debug) {
+                    String miDebugger = (String) launchArguments.get("miDebugger");
+                    startNativeDebug(nativeImageFile, args, miDebugger, context, executionDescriptor, launchFuture);
+                } else {
+                    execNative(nativeImageFile, args, context, executionDescriptor, launchFuture);//, success);
+                }
+            });
+        }
+        return launchFuture;
+    }
 
-            Lookup lookup;
-            if (singleMethod != null) {
-                lookup = Lookups.fixed(toRun, singleMethod, params, ioContext, progress);
-            } else {
-                lookup = Lookups.fixed(toRun, ioContext, params, progress);
+    private static void execNative(File nativeImageFile, List<String> args, DebugAdapterContext context, ExecutionDescriptor executionDescriptor, CompletableFuture<Void> launchFuture) {
+        ExecutionService.newService(() -> {
+            launchFuture.complete(null);
+            List<String> command = args.isEmpty() ? Collections.singletonList(nativeImageFile.getAbsolutePath()) : join(nativeImageFile.getAbsolutePath(), args);
+            try {
+                return new ProcessBuilder(command).start();
+            } catch (IOException ex) {
+                ErrorUtilities.completeExceptionally(launchFuture,
+                    "Failed to run debuggee native image: " + ex.getLocalizedMessage(),
+                    ResponseErrorCode.serverErrorStart);
+                throw ex;
             }
-            Lookups.executeWith(launchCtx, () -> {
-                providerAndCommand.first().invokeAction(providerAndCommand.second(), lookup);
+        }, executionDescriptor, "Run - " + nativeImageFile.getName()).run();
+    }
+
+    private static List<String> join(String first, List<String> next) {
+        List<String> joined = new ArrayList<>(next.size() + 1);
+        joined.add(first);
+        joined.addAll(next);
+        return joined;
+    }
 
+    private static void startNativeDebug(File nativeImageFile, List<String> args, String miDebugger, DebugAdapterContext context, ExecutionDescriptor executionDescriptor, CompletableFuture<Void> launchFuture) {
+        AtomicReference<NIDebugger> niDebuggerRef = new AtomicReference<>();
+        AtomicReference<NbDebugSession> debugSessionRef = new AtomicReference<>();
+        NIDebugger niDebugger;
+        try {
+            niDebugger = NIDebugRunner.start(nativeImageFile, args, miDebugger, null, null, executionDescriptor, engine -> {
+                Session session = engine.lookupFirst(null, Session.class);
+                NbDebugSession debugSession = new NbDebugSession(session);
+                debugSessionRef.set(debugSession);
+                NIDebugger niDebugger_ = niDebuggerRef.get();
+                if (niDebugger_ != null) {
+                    debugSession.setNIDebugger(niDebugger_);
+                }
+                context.setDebugSession(debugSession);
+                launchFuture.complete(null);
+                context.getConfigurationSemaphore().waitForConfigurationDone();
             });
-        }).exceptionally((t) -> {
-            launchFuture.completeExceptionally(t);
-            return null;
-        });
-        return launchFuture;
+        } catch (IllegalStateException ex) {
+            ErrorUtilities.completeExceptionally(launchFuture,
+                "Failed to launch debuggee native image. " + ex.getLocalizedMessage(),
+                ResponseErrorCode.serverErrorStart);
+            return ;
+        }
+        niDebuggerRef.set(niDebugger);
+        NbDebugSession debugSession = debugSessionRef.get();
+        if (debugSession != null) {
+            debugSession.setNIDebugger(niDebugger);
+        }
     }
-    
+
     @NonNull
     private List<String> argsToStringList(Object o) {
         if (o == null) {
@@ -186,7 +267,7 @@ public abstract class NbLaunchDelegate {
         }
     }
 
-    private CompletableFuture<Pair<ActionProvider, String>> findTargetWithPossibleRebuild(FileObject toRun, SingleMethod singleMethod, boolean debug, boolean testRun, NbProcessConsole ioContext) throws IllegalArgumentException {
+    private static CompletableFuture<Pair<ActionProvider, String>> findTargetWithPossibleRebuild(FileObject toRun, SingleMethod singleMethod, boolean debug, boolean testRun, NbProcessConsole ioContext) throws IllegalArgumentException {
         Pair<ActionProvider, String> providerAndCommand = findTarget(toRun, singleMethod, debug, testRun);
         if (providerAndCommand != null) {
             return CompletableFuture.completedFuture(providerAndCommand);
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbLaunchRequestHandler.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbLaunchRequestHandler.java
index 51072b5..b20629b 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbLaunchRequestHandler.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbLaunchRequestHandler.java
@@ -53,6 +53,7 @@ public final class NbLaunchRequestHandler {
     private NbLaunchDelegate activeLaunchHandler;
 
     public CompletableFuture<Void> launch(Map<String, Object> launchArguments, DebugAdapterContext context) {
+        boolean isNative = "nativeimage".equals(launchArguments.get("type"));
         CompletableFuture<Void> resultFuture = new CompletableFuture<>();
         boolean noDebug = (Boolean) launchArguments.getOrDefault("noDebug", Boolean.FALSE);
         Consumer<DebugAdapterContext> terminateHandle = (daContext) -> handleTerminatedEvent(daContext);
@@ -61,8 +62,8 @@ public final class NbLaunchRequestHandler {
         // validation
         List<String> modulePaths = (List<String>) launchArguments.getOrDefault("modulePaths", Collections.emptyList());
         List<String> classPaths = (List<String>) launchArguments.getOrDefault("classPaths", Collections.emptyList());
-        if (StringUtils.isBlank((String)launchArguments.get("mainClass"))
-                || modulePaths.isEmpty() && classPaths.isEmpty()) {
+        if (!isNative && (StringUtils.isBlank((String)launchArguments.get("mainClass"))
+                          || modulePaths.isEmpty() && classPaths.isEmpty())) {
             ErrorUtilities.completeExceptionally(resultFuture,
                 "Failed to launch debuggee VM. Missing mainClass or modulePaths/classPaths options in launch configuration.",
                 ResponseErrorCode.serverErrorStart);
@@ -80,37 +81,52 @@ public final class NbLaunchRequestHandler {
             context.setDebuggeeEncoding(Charset.forName((String)launchArguments.get("encoding")));
         }
 
-        if (StringUtils.isBlank((String)launchArguments.get("vmArgs"))) {
-            launchArguments.put("vmArgs", String.format("-Dfile.encoding=%s", context.getDebuggeeEncoding().name()));
-        } else {
-            // if vmArgs already has the file.encoding settings, duplicate options for jvm will not cause an error, the right most value wins
-            launchArguments.put("vmArgs", String.format("%s -Dfile.encoding=%s", launchArguments.get("vmArgs"), context.getDebuggeeEncoding().name()));
+        if (!isNative) {
+            if (StringUtils.isBlank((String)launchArguments.get("vmArgs"))) {
+                launchArguments.put("vmArgs", String.format("-Dfile.encoding=%s", context.getDebuggeeEncoding().name()));
+            } else {
+                // if vmArgs already has the file.encoding settings, duplicate options for jvm will not cause an error, the right most value wins
+                launchArguments.put("vmArgs", String.format("%s -Dfile.encoding=%s", launchArguments.get("vmArgs"), context.getDebuggeeEncoding().name()));
+            }
         }
         context.setDebugMode(!noDebug);
 
         activeLaunchHandler.preLaunch(launchArguments, context);
 
         String filePath = (String)launchArguments.get("mainClass");
-        File ioFile = null;
-        if (filePath != null) {
-            ioFile = new File(filePath);
-            if (!ioFile.exists()) {
-                try {
-                    URI uri = new URI(filePath);
-                    ioFile = Utilities.toFile(uri);
-                } catch (URISyntaxException ex) {
-                    // Not a valid file
+        FileObject file = null;
+        File nativeImageFile = null;
+        if (!isNative) {
+            File ioFile = null;
+            if (filePath != null) {
+                ioFile = new File(filePath);
+                if (!ioFile.exists()) {
+                    try {
+                        URI uri = new URI(filePath);
+                        ioFile = Utilities.toFile(uri);
+                    } catch (URISyntaxException ex) {
+                        // Not a valid file
+                    }
                 }
             }
-        }
-        FileObject file = ioFile != null ? FileUtil.toFileObject(ioFile) : null;
-        if (file == null) {
-            ErrorUtilities.completeExceptionally(resultFuture,
-                    "Missing file: " + filePath,
+            file = ioFile != null ? FileUtil.toFileObject(ioFile) : null;
+            if (file == null) {
+                ErrorUtilities.completeExceptionally(resultFuture,
+                        "Missing file: " + filePath,
+                        ResponseErrorCode.serverErrorStart);
+                return resultFuture;
+            }
+        } else {
+            String nativeImage = (String) launchArguments.get("nativeImagePath");
+            if (nativeImage == null) {
+                ErrorUtilities.completeExceptionally(resultFuture,
+                    "Failed to launch debuggee native image. No native image is specified.",
                     ResponseErrorCode.serverErrorStart);
-            return resultFuture;
+                return resultFuture;
+            }
+            nativeImageFile = new File(nativeImage);
         }
-        if (!launchArguments.containsKey("sourcePaths")) {
+        if (!isNative && !launchArguments.containsKey("sourcePaths")) {
             ClassPath sourceCP = ClassPath.getClassPath(file, ClassPath.SOURCE);
             if (sourceCP != null) {
                 FileObject[] roots = sourceCP.getRoots();
@@ -125,7 +141,7 @@ public final class NbLaunchRequestHandler {
         }
         String singleMethod = (String)launchArguments.get("methodName");
         boolean testRun = (Boolean) launchArguments.getOrDefault("testRun", Boolean.FALSE);
-        activeLaunchHandler.nbLaunch(file, singleMethod, launchArguments, context, !noDebug, testRun, new OutputListener(context)).thenRun(() -> {
+        activeLaunchHandler.nbLaunch(file, nativeImageFile, singleMethod, launchArguments, context, !noDebug, testRun, new OutputListener(context)).thenRun(() -> {
             activeLaunchHandler.postLaunch(launchArguments, context);
             resultFuture.complete(null);
         }).exceptionally(e -> {
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/variables/NbVariablesRequestHandler.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/variables/NbVariablesRequestHandler.java
index bd76604..b1627d2 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/variables/NbVariablesRequestHandler.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/variables/NbVariablesRequestHandler.java
@@ -28,8 +28,8 @@ import org.eclipse.lsp4j.debug.Variable;
 import org.eclipse.lsp4j.debug.VariablesArguments;
 import org.eclipse.lsp4j.debug.VariablesResponse;
 import org.eclipse.lsp4j.jsonrpc.messages.ResponseErrorCode;
+import org.netbeans.api.debugger.Session;
 
-import org.netbeans.api.debugger.jpda.JPDADebugger;
 import org.netbeans.modules.java.lsp.server.debugging.DebugAdapterContext;
 import org.netbeans.modules.java.lsp.server.debugging.NbScope;
 import org.netbeans.modules.java.lsp.server.debugging.launch.NbDebugSession;
@@ -63,8 +63,8 @@ public final class NbVariablesRequestHandler {
             // Nothing, or an old container
             response.setVariables(new Variable[0]);
         } else {
-            JPDADebugger debugger = context.getDebugSession().getDebugger();
-            Models.CompoundModel localsModel = localsModelProvider.getModel(debugger.getSession());
+            Session session = context.getDebugSession().getSession();
+            Models.CompoundModel localsModel = localsModelProvider.getModel(session);
             int threadId;
             if (container instanceof NbScope) {
                 threadId = ((NbScope) container).getFrame().getThreadId();
@@ -84,14 +84,21 @@ public final class NbVariablesRequestHandler {
                 }
                 for (Object child : children) {
                     String name = localsModel.getDisplayName(child);
-                    String value = String.valueOf(localsModel.getValueAt(child, LOCALS_TO_STRING_COLUMN_ID));
+                    String value;
+                    try {
+                        value = String.valueOf(localsModel.getValueAt(child, LOCALS_TO_STRING_COLUMN_ID));
+                    } catch (UnknownTypeException ex) {
+                        value = String.valueOf(localsModel.getValueAt(child, LOCALS_VALUE_COLUMN_ID));
+                    }
                     String type = String.valueOf(localsModel.getValueAt(child, LOCALS_TYPE_COLUMN_ID));
-                    int id = context.getThreadsProvider().getThreadObjects().addObject(threadId, child);
                     Variable variable = new Variable();
                     variable.setName(name);
                     variable.setValue(value);
                     variable.setType(type);
-                    variable.setVariablesReference(id);
+                    if (!localsModel.isLeaf(child)) {
+                        int id = context.getThreadsProvider().getThreadObjects().addObject(threadId, child);
+                        variable.setVariablesReference(id);
+                    }
                     list.add(variable);
                 }
             } catch (UnknownTypeException e) {
@@ -132,8 +139,8 @@ public final class NbVariablesRequestHandler {
             return future;
         }
 
-        JPDADebugger debugger = ((NbDebugSession) context.getDebugSession()).getDebugger();
-        Models.CompoundModel localsModel = localsModelProvider.getModel(debugger.getSession());
+        Session session = ((NbDebugSession) context.getDebugSession()).getSession();
+        Models.CompoundModel localsModel = localsModelProvider.getModel(session);
 
         int threadId;
         if (container instanceof NbScope) {
diff --git a/java/java.lsp.server/vscode/package.json b/java/java.lsp.server/vscode/package.json
index 45b3736..f63cec2 100644
--- a/java/java.lsp.server/vscode/package.json
+++ b/java/java.lsp.server/vscode/package.json
@@ -148,6 +148,61 @@
 						}
 					}
 				]
+			},
+			{
+				"type": "nativeimage",
+				"label": "Native Image",
+				"runtime": "node",
+				"languages": [
+					"java"
+				],
+				"configurationAttributes": {
+					"launch": {
+						"required": [
+							"nativeImagePath"
+						],
+						"properties": {
+							"nativeImagePath": {
+								"type": "string",
+								"description": "Absolute path to the application native image.",
+								"default": "${workspaceFolder}/build/native-image/application"
+							},
+							"miDebugger": {
+								"type": "string",
+								"description": "MI Debugger",
+								"default": "gdb"
+							},
+							"console": {
+								"type": "string",
+								"enum": [
+									"internalConsole"
+								],
+								"description": "The specified console to launch the program.",
+								"default": "internalConsole"
+							}
+						}
+					}
+				},
+				"initialConfigurations": [
+					{
+						"type": "nativeimage",
+						"request": "launch",
+						"name": "Launch Native Image",
+						"nativeImagePath": "${workspaceFolder}/build/native-image/application"
+					}
+				],
+				"configurationSnippets": [
+					{
+						"label": "Launch Native Image",
+						"description": "Launch a native image with MI debugger.",
+						"body": {
+							"type": "nativeimage",
+							"request": "launch",
+							"name": "Launch Native Image",
+							"nativeImagePath": "^\"${1:\\${workspaceFolder\\}/build/native-image/application}\""
+						}
+					}
+				]
 			}
 		],
 		"commands": [
diff --git a/java/java.lsp.server/vscode/src/extension.ts b/java/java.lsp.server/vscode/src/extension.ts
index bc83afe..e0a6bfb 100644
--- a/java/java.lsp.server/vscode/src/extension.ts
+++ b/java/java.lsp.server/vscode/src/extension.ts
@@ -174,9 +174,12 @@ export function activate(context: ExtensionContext): VSNetBeansAPI {
     //register debugger:
     let configProvider = new NetBeansConfigurationProvider();
     context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('java8+', configProvider));
+    let configNativeProvider = new NetBeansConfigurationNativeProvider();
+    context.subscriptions.push(vscode.debug.registerDebugConfigurationProvider('nativeimage', configNativeProvider));
 
     let debugDescriptionFactory = new NetBeansDebugAdapterDescriptionFactory();
     context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('java8+', debugDescriptionFactory));
+    context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('nativeimage', debugDescriptionFactory));
 
     // register commands
     context.subscriptions.push(commands.registerCommand('java.workspace.compile', () => {
@@ -655,3 +658,26 @@ class NetBeansConfigurationProvider implements vscode.DebugConfigurationProvider
         return config;
     }
 }
+
+class NetBeansConfigurationNativeProvider implements vscode.DebugConfigurationProvider {
+
+    resolveDebugConfiguration(_folder: vscode.WorkspaceFolder | undefined, config: vscode.DebugConfiguration, _token?: vscode.CancellationToken): vscode.ProviderResult<vscode.DebugConfiguration> {
+        if (!config.type) {
+            config.type = 'nativeimage';
+        }
+        if (!config.request) {
+            config.request = 'launch';
+        }
+        if (!config.nativeImagePath) {
+            config.nativeImagePath = '${workspaceFolder}/build/native-image/application';
+        }
+        if (!config.miDebugger) {
+            config.miDebugger = 'gdb';
+        }
+        if (!config.console) {
+            config.console = 'internalConsole';
+        }
+
+        return config;
+    }
+}
diff --git a/java/java.nativeimage.debugger/build.xml b/java/java.nativeimage.debugger/build.xml
new file mode 100644
index 0000000..6f55714
--- /dev/null
+++ b/java/java.nativeimage.debugger/build.xml
@@ -0,0 +1,28 @@
+<?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.
+
+-->
+<!-- You may freely edit this file. See harness/README in the NetBeans platform -->
+<!-- for some information on what you could do (e.g. targets to override). -->
+<!-- If you delete this file and reopen the project it will be recreated. -->
+<project name="java/java.nativeimage.debugger" default="netbeans" basedir=".">
+    <description>Builds, tests, and runs the project org.netbeans.modules.java.nativeimage.debugger.</description>
+    <import file="../../nbbuild/templates/projectized.xml"/>
+</project>
diff --git a/java/java.nativeimage.debugger/manifest.mf b/java/java.nativeimage.debugger/manifest.mf
new file mode 100644
index 0000000..7fd02de
--- /dev/null
+++ b/java/java.nativeimage.debugger/manifest.mf
@@ -0,0 +1,7 @@
+Manifest-Version: 1.0
+AutoUpdate-Show-In-Client: false
+OpenIDE-Module: org.netbeans.modules.java.nativeimage.debugger/0
+OpenIDE-Module-Layer: org/netbeans/modules/java/nativeimage/debugger/resources/mf-layer.xml
+OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/java/nativeimage/debugger/Bundle.properties
+OpenIDE-Module-Specification-Version: 0.1
+
diff --git a/java/java.nativeimage.debugger/nbproject/project.properties b/java/java.nativeimage.debugger/nbproject/project.properties
new file mode 100644
index 0000000..5137752
--- /dev/null
+++ b/java/java.nativeimage.debugger/nbproject/project.properties
@@ -0,0 +1,19 @@
+# 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.source=1.8
+javac.compilerargs=-Xlint -Xlint:-serial
diff --git a/cpplite/cpplite.debugger/nbproject/project.xml b/java/java.nativeimage.debugger/nbproject/project.xml
similarity index 78%
copy from cpplite/cpplite.debugger/nbproject/project.xml
copy to java/java.nativeimage.debugger/nbproject/project.xml
index d360c06..21f442c 100644
--- a/cpplite/cpplite.debugger/nbproject/project.xml
+++ b/java/java.nativeimage.debugger/nbproject/project.xml
@@ -23,7 +23,7 @@
     <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.cpplite.debugger</code-name-base>
+            <code-name-base>org.netbeans.modules.java.nativeimage.debugger</code-name-base>
             <module-dependencies>
                 <dependency>
                     <code-name-base>org.netbeans.api.annotations.common</code-name-base>
@@ -44,74 +44,96 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
-                    <code-name-base>org.netbeans.modules.dlight.nativeexecution</code-name-base>
+                    <code-name-base>org.netbeans.api.debugger.jpda</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
-                        <specification-version>1.48</specification-version>
+                        <release-version>2</release-version>
+                        <specification-version>3.20</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
-                    <code-name-base>org.netbeans.modules.projectapi</code-name-base>
+                    <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.75</specification-version>
+                        <specification-version>1.65</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
-                    <code-name-base>org.netbeans.spi.debugger.ui</code-name-base>
+                    <code-name-base>org.netbeans.modules.extexecution</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>2</release-version>
+                        <specification-version>1.59</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>2.62</specification-version>
+                        <specification-version>1.83</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
-                    <code-name-base>org.netbeans.spi.viewmodel</code-name-base>
+                    <code-name-base>org.netbeans.modules.nativeimage.api</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
-                        <release-version>2</release-version>
-                        <specification-version>1.59</specification-version>
+                        <release-version>0</release-version>
+                        <specification-version>0.1</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
-                    <code-name-base>org.openide.awt</code-name-base>
+                    <code-name-base>org.netbeans.modules.projectapi</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
-                        <specification-version>7.76</specification-version>
+                        <release-version>1</release-version>
+                        <specification-version>1.75</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
-                    <code-name-base>org.openide.filesystems</code-name-base>
+                    <code-name-base>org.netbeans.modules.projectuiapi</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
-                        <specification-version>9.18</specification-version>
+                        <release-version>1</release-version>
+                        <specification-version>1.99</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
-                    <code-name-base>org.openide.loaders</code-name-base>
+                    <code-name-base>org.netbeans.spi.debugger.ui</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
-                        <specification-version>7.76</specification-version>
+                        <release-version>1</release-version>
+                        <specification-version>2.62</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
-                    <code-name-base>org.openide.modules</code-name-base>
+                    <code-name-base>org.netbeans.spi.viewmodel</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
-                        <specification-version>7.56</specification-version>
+                        <release-version>2</release-version>
+                        <specification-version>1.59</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.awt</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>7.76</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
-                    <code-name-base>org.openide.nodes</code-name-base>
+                    <code-name-base>org.openide.dialogs</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
@@ -119,43 +141,43 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
-                    <code-name-base>org.openide.text</code-name-base>
+                    <code-name-base>org.openide.filesystems</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
-                        <specification-version>6.75</specification-version>
+                        <specification-version>9.18</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
-                    <code-name-base>org.openide.util</code-name-base>
+                    <code-name-base>org.openide.modules</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
-                        <specification-version>9.15</specification-version>
+                        <specification-version>7.60</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
-                    <code-name-base>org.openide.util.lookup</code-name-base>
+                    <code-name-base>org.openide.util</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
-                        <specification-version>8.41</specification-version>
+                        <specification-version>9.15</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
-                    <code-name-base>org.openide.util.ui</code-name-base>
+                    <code-name-base>org.openide.util.lookup</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
-                        <specification-version>9.16</specification-version>
+                        <specification-version>8.41</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
-                    <code-name-base>org.openide.windows</code-name-base>
+                    <code-name-base>org.openide.util.ui</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
-                        <specification-version>6.85</specification-version>
+                        <specification-version>9.20</specification-version>
                     </run-dependency>
                 </dependency>
             </module-dependencies>
@@ -186,13 +208,9 @@
                 </test-type>
             </test-dependencies>
             <friend-packages>
-                <friend>org.netbeans.modules.cpplite.project</friend>
-                <package>org.netbeans.modules.cpplite.debugger.api</package>
+                <friend>org.netbeans.modules.java.lsp.server</friend>
+                <package>org.netbeans.modules.java.nativeimage.debugger.api</package>
             </friend-packages>
-            <class-path-extension>
-                <runtime-relative-path>ext/cpplite-mi-f8f8250283be.jar</runtime-relative-path>
-                <binary-origin>external/cpplite-mi-f8f8250283be.jar</binary-origin>
-            </class-path-extension>
         </data>
     </configuration>
 </project>
diff --git a/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/Bundle.properties b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/Bundle.properties
new file mode 100644
index 0000000..9afa08a
--- /dev/null
+++ b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/Bundle.properties
@@ -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.
+
+# manifest's description entries
+OpenIDE-Module-Display-Category=Debugging
+OpenIDE-Module-Name=JVM Native Image Debugger
diff --git a/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/actions/Bundle.properties b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/actions/Bundle.properties
new file mode 100644
index 0000000..d033bba
--- /dev/null
+++ b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/actions/Bundle.properties
@@ -0,0 +1,21 @@
+# 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.
+
+NIAttachCustomizer.fileLabel.text=Image File:
+NIAttachCustomizer.fileTextField.text=
+NIAttachCustomizer.fileButton.text=Browse
+NIAttachCustomizer.dbgLabel.text=Debugger:
diff --git a/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/actions/NIAttachCustomizer.form b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/actions/NIAttachCustomizer.form
new file mode 100644
index 0000000..541b070
--- /dev/null
+++ b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/actions/NIAttachCustomizer.form
@@ -0,0 +1,125 @@
+<?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.9" 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="true"/>
+    <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" attributes="0">
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="0" attributes="0">
+                  <Group type="102" alignment="0" attributes="0">
+                      <Component id="fileLabel" min="-2" max="-2" attributes="0"/>
+                      <EmptySpace max="-2" attributes="0"/>
+                      <Component id="fileTextField" max="32767" attributes="0"/>
+                  </Group>
+                  <Group type="102" alignment="0" attributes="0">
+                      <Component id="dbgLabel" min="-2" max="-2" attributes="0"/>
+                      <EmptySpace max="-2" attributes="0"/>
+                      <Component id="dbgComboBox" pref="196" max="32767" attributes="0"/>
+                  </Group>
+              </Group>
+              <EmptySpace max="-2" attributes="0"/>
+              <Component id="fileButton" min="-2" max="-2" 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">
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="fileLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="fileTextField" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="fileButton" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace max="-2" attributes="0"/>
+              <Group type="103" groupAlignment="3" attributes="0">
+                  <Component id="dbgLabel" alignment="3" min="-2" max="-2" attributes="0"/>
+                  <Component id="dbgComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
+              </Group>
+              <EmptySpace max="32767" attributes="0"/>
+          </Group>
+      </Group>
+    </DimensionLayout>
+  </Layout>
+  <SubComponents>
+    <Component class="javax.swing.JLabel" name="fileLabel">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/java/nativeimage/debugger/actions/Bundle.properties" key="NIAttachCustomizer.fileLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JTextField" name="fileTextField">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/java/nativeimage/debugger/actions/Bundle.properties" key="NIAttachCustomizer.fileTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JButton" name="fileButton">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/java/nativeimage/debugger/actions/Bundle.properties" key="NIAttachCustomizer.fileButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="fileButtonActionPerformed"/>
+      </Events>
+    </Component>
+    <Component class="javax.swing.JLabel" name="dbgLabel">
+      <Properties>
+        <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+          <ResourceString bundle="org/netbeans/modules/java/nativeimage/debugger/actions/Bundle.properties" key="NIAttachCustomizer.dbgLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </Property>
+      </Properties>
+    </Component>
+    <Component class="javax.swing.JComboBox" name="dbgComboBox">
+      <Properties>
+        <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
+          <StringArray count="2">
+            <StringItem index="0" value="gdb"/>
+            <StringItem index="1" value="lldb-mi"/>
+          </StringArray>
+        </Property>
+      </Properties>
+      <AuxValues>
+        <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
+      </AuxValues>
+    </Component>
+  </SubComponents>
+</Form>
diff --git a/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/actions/NIAttachCustomizer.java b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/actions/NIAttachCustomizer.java
new file mode 100644
index 0000000..0d1fe63
--- /dev/null
+++ b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/actions/NIAttachCustomizer.java
@@ -0,0 +1,378 @@
+/*
+ * 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.java.nativeimage.debugger.actions;
+
+import java.awt.event.ActionEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
+import javax.swing.Action;
+import javax.swing.JFileChooser;
+import javax.swing.SwingUtilities;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.filechooser.FileFilter;
+
+import org.netbeans.api.debugger.Properties;
+import org.netbeans.api.project.FileOwnerQuery;
+import org.netbeans.api.project.Project;
+import org.netbeans.modules.java.nativeimage.debugger.api.NIDebugRunner;
+import org.netbeans.spi.debugger.ui.Controller;
+import static org.netbeans.spi.debugger.ui.Controller.PROP_VALID;
+import org.netbeans.spi.debugger.ui.PersistentController;
+import static org.netbeans.spi.project.ActionProvider.COMMAND_DEBUG;
+import org.openide.DialogDisplayer;
+import org.openide.NotifyDescriptor;
+import org.openide.awt.Actions;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.modules.ModuleInfo;
+import org.openide.modules.Modules;
+import org.openide.util.Exceptions;
+import org.openide.util.NbBundle;
+import org.openide.util.RequestProcessor;
+import org.openide.util.Utilities;
+
+/**
+ *
+ * @author martin
+ */
+public class NIAttachCustomizer extends javax.swing.JPanel {
+
+    private final ConnectController controller;
+    private final ValidityDocumentListener validityDocumentListener = new ValidityDocumentListener();
+    private final RequestProcessor currentFileRP = new RequestProcessor(NIAttachCustomizer.class);
+
+    /**
+     * Creates new form NIAttachCustomizer
+     */
+    public NIAttachCustomizer() {
+        controller = new ConnectController();
+        initComponents();
+        fileTextField.getDocument().addDocumentListener(validityDocumentListener);
+        initNIFile();
+    }
+
+    private void initNIFile() {
+        currentFileRP.post(() -> {
+            FileObject currentFO = Utilities.actionsGlobalContext().lookup(FileObject.class);
+            if (currentFO != null) {
+                File currentFile = FileUtil.toFile(currentFO);
+                String path;
+                if (currentFile != null && currentFile.canExecute()) {
+                    path = currentFile.getAbsolutePath();
+                } else {
+                    Project project = FileOwnerQuery.getOwner(currentFO);
+                    if (project != null) {
+                        currentFO = project.getProjectDirectory();
+                        currentFile = FileUtil.toFile(currentFO);
+                        path = currentFile.getAbsolutePath();
+                    } else {
+                        path = null;
+                    }
+                }
+                if (path != null) {
+                    SwingUtilities.invokeLater(() -> {
+                        fileTextField.setText(path);
+                    });
+                }
+            }
+        });
+    }
+
+    /**
+     * 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.
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+
+        fileLabel = new javax.swing.JLabel();
+        fileTextField = new javax.swing.JTextField();
+        fileButton = new javax.swing.JButton();
+        dbgLabel = new javax.swing.JLabel();
+        dbgComboBox = new javax.swing.JComboBox<>();
+
+        org.openide.awt.Mnemonics.setLocalizedText(fileLabel, org.openide.util.NbBundle.getMessage(NIAttachCustomizer.class, "NIAttachCustomizer.fileLabel.text")); // NOI18N
+
+        fileTextField.setText(org.openide.util.NbBundle.getMessage(NIAttachCustomizer.class, "NIAttachCustomizer.fileTextField.text")); // NOI18N
+
+        org.openide.awt.Mnemonics.setLocalizedText(fileButton, org.openide.util.NbBundle.getMessage(NIAttachCustomizer.class, "NIAttachCustomizer.fileButton.text")); // NOI18N
+        fileButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                fileButtonActionPerformed(evt);
+            }
+        });
+
+        org.openide.awt.Mnemonics.setLocalizedText(dbgLabel, org.openide.util.NbBundle.getMessage(NIAttachCustomizer.class, "NIAttachCustomizer.dbgLabel.text")); // NOI18N
+
+        dbgComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "gdb", "lldb-mi" }));
+
+        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()
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addGroup(layout.createSequentialGroup()
+                        .addComponent(fileLabel)
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                        .addComponent(fileTextField))
+                    .addGroup(layout.createSequentialGroup()
+                        .addComponent(dbgLabel)
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                        .addComponent(dbgComboBox, 0, 196, Short.MAX_VALUE)))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addComponent(fileButton)
+                .addContainerGap())
+        );
+        layout.setVerticalGroup(
+            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+            .addGroup(layout.createSequentialGroup()
+                .addContainerGap()
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(fileLabel)
+                    .addComponent(fileTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+                    .addComponent(fileButton))
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+                    .addComponent(dbgLabel)
+                    .addComponent(dbgComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+        );
+    }// </editor-fold>//GEN-END:initComponents
+
+    @NbBundle.Messages({"CTL_ExecutableFiles=Executable Files"})
+    private void fileButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fileButtonActionPerformed
+        JFileChooser chooser = new JFileChooser(fileTextField.getText());
+        chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
+        chooser.setFileFilter(new FileFilter() {
+            @Override
+            public boolean accept(File f) {
+                return f.isDirectory() || f.canExecute();
+            }
+
+            @Override
+            public String getDescription() {
+                return Bundle.CTL_ExecutableFiles();
+            }
+        });
+        if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
+            fileTextField.setText(chooser.getSelectedFile().getAbsolutePath());
+        }
+    }//GEN-LAST:event_fileButtonActionPerformed
+
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JComboBox<String> dbgComboBox;
+    private javax.swing.JLabel dbgLabel;
+    private javax.swing.JButton fileButton;
+    private javax.swing.JLabel fileLabel;
+    private javax.swing.JTextField fileTextField;
+    // End of variables declaration//GEN-END:variables
+
+    RequestProcessor.Task validationTask = currentFileRP.create(new FileValidationTask());
+
+    @NbBundle.Messages({"MSG_NoFile=Native Imige File is missing."})
+    private void checkValid() {
+        assert SwingUtilities.isEventDispatchThread() : "Called outside of AWT.";
+        if (fileTextField.getText().isEmpty()) {
+            controller.setInformationMessage(Bundle.MSG_NoFile());
+            controller.setValid(false);
+            return ;
+        }
+        validationTask.schedule(200);
+    }
+
+    private class FileValidationTask implements Runnable {
+
+        @Override
+        public void run() {
+            String filePath = fileTextField.getText();
+            File file = new File(filePath);
+            boolean canExecute = file.isFile() && file.canExecute();
+            SwingUtilities.invokeLater(() -> {
+                controller.setValid(canExecute);
+                String message = canExecute ? null : Bundle.MSG_NoFile();
+                controller.setInformationMessage(message);
+            });
+        }
+    }
+
+    private class ValidityDocumentListener implements DocumentListener {
+        @Override
+        public void insertUpdate(DocumentEvent e) {
+            checkValid();
+        }
+        @Override
+        public void removeUpdate(DocumentEvent e) {
+            checkValid();
+        }
+        @Override
+        public void changedUpdate(DocumentEvent e) {
+            checkValid();
+        }
+    }
+
+    Controller getController() {
+        return controller;
+    }
+
+    private static final String CPPLITE_DEBUGGER = "org.netbeans.modules.cpplite.debugger"; // NOI18N
+
+    @NbBundle.Messages({"MSG_EnableNativeDebugger=Enable {0} in Plugins Manager", "TTL_EnableNativeDebugger=Native Debugger Dependency"})
+    private static boolean checkCPPLite() {
+        ModuleInfo cppliteDebugger = Modules.getDefault().findCodeNameBase(CPPLITE_DEBUGGER);
+        if (cppliteDebugger != null && !cppliteDebugger.isEnabled()) {
+            Action pluginsManager = Actions.forID("System", "org.netbeans.modules.autoupdate.ui.actions.PluginManagerAction");
+            String moduleDisplayName = cppliteDebugger.getDisplayName();
+            NotifyDescriptor messageDescriptor = new NotifyDescriptor.Confirmation(
+                    Bundle.MSG_EnableNativeDebugger(moduleDisplayName),
+                    Bundle.TTL_EnableNativeDebugger(),
+                    NotifyDescriptor.OK_CANCEL_OPTION);
+            if (NotifyDescriptor.OK_OPTION.equals(DialogDisplayer.getDefault().notify(messageDescriptor))) {
+                SwingUtilities.invokeLater(() -> {
+                    ActionEvent ev = new ActionEvent(pluginsManager, 100, "installed");
+                    pluginsManager.actionPerformed(ev);
+                });
+            }
+            return false;
+        }
+        return true;
+    }
+
+    public class ConnectController implements PersistentController {
+
+        private static final String NI_ATTACH_PROPERTIES = "native_image_attach_settings";
+        private static final String PROP_NI_FILE = "niFile";
+        private static final String PROP_DBG = "debugger";
+
+        private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
+        private boolean valid = true;
+
+        @Override
+        public String getDisplayName() {
+            return dbgComboBox.getSelectedItem() + " " + new File(fileTextField.getText()).getName();
+        }
+
+        @Override
+        public boolean load(Properties props) {
+            assert !SwingUtilities.isEventDispatchThread();
+            final Properties attachProps = props.getProperties(NI_ATTACH_PROPERTIES);
+            try {
+                SwingUtilities.invokeAndWait(new Runnable() {
+                    @Override
+                    public void run() {
+                        fileTextField.setText(attachProps.getString(PROP_NI_FILE, ""));
+                        dbgComboBox.setSelectedItem(attachProps.getString(PROP_DBG, "DBG"));
+                    }
+                });
+            } catch (InterruptedException | InvocationTargetException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+            return true;
+        }
+
+        @Override
+        public void save(Properties props) {
+            final Properties attachProps = props.getProperties(NI_ATTACH_PROPERTIES);
+            if (SwingUtilities.isEventDispatchThread()) {
+                saveToProps(attachProps);
+            } else {
+                try {
+                    SwingUtilities.invokeAndWait(new Runnable() {
+                        @Override
+                        public void run() {
+                            saveToProps(attachProps);
+
+                        }
+                    });
+                } catch (InterruptedException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (InvocationTargetException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+            }
+        }
+
+        private void saveToProps(Properties attachProps) {
+            attachProps.setString(PROP_NI_FILE, fileTextField.getText());
+            attachProps.setString(PROP_DBG, (String) dbgComboBox.getSelectedItem());
+        }
+
+        @Override
+        public boolean ok() {
+            String filePath = fileTextField.getText();
+            String debuggerCommand = dbgComboBox.getSelectedItem().toString();
+            currentFileRP.post(() -> {
+                if (!checkCPPLite()) {
+                    return ;
+                }
+                File file = new File(filePath);
+                String displayName = COMMAND_DEBUG + " " + file.getName();
+                NIDebugRunner.start(file, Collections.emptyList(), debuggerCommand, null, displayName, null, null);
+            });
+            return true;
+        }
+
+        @Override
+        public boolean cancel() {
+            return true;
+        }
+
+        @Override
+        public boolean isValid() {
+            return valid;
+        }
+
+        void setValid(boolean valid) {
+            this.valid = valid;
+            firePropertyChange(PROP_VALID, !valid, valid);
+        }
+
+        void setErrorMessage(String msg) {
+            firePropertyChange(NotifyDescriptor.PROP_ERROR_NOTIFICATION, null, msg);
+        }
+
+        void setInformationMessage(String msg) {
+            firePropertyChange(NotifyDescriptor.PROP_INFO_NOTIFICATION, null, msg);
+        }
+
+        private void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
+            pcs.firePropertyChange(propertyName, oldValue, newValue);
+        }
+
+        @Override
+        public void addPropertyChangeListener(PropertyChangeListener l) {
+            pcs.addPropertyChangeListener(l);
+        }
+
+        @Override
+        public void removePropertyChangeListener(PropertyChangeListener l) {
+            pcs.removePropertyChangeListener(l);
+        }
+
+    }
+
+}
diff --git a/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/actions/NIAttachType.java b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/actions/NIAttachType.java
new file mode 100644
index 0000000..ba21ff0
--- /dev/null
+++ b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/actions/NIAttachType.java
@@ -0,0 +1,56 @@
+/*
+ * 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.java.nativeimage.debugger.actions;
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import javax.swing.JComponent;
+
+import org.netbeans.spi.debugger.ui.AttachType;
+import org.netbeans.spi.debugger.ui.Controller;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author martin
+ */
+@NbBundle.Messages("CTL_NIConnector_name=Native Image")
+@AttachType.Registration(displayName="#CTL_NIConnector_name")
+public class NIAttachType extends AttachType {
+
+    private Reference<NIAttachCustomizer> customizerRef = new WeakReference<>(null);
+
+    @Override
+    public JComponent getCustomizer() {
+        NIAttachCustomizer ac = new NIAttachCustomizer();
+        customizerRef = new WeakReference<>(ac);
+        return ac;
+    }
+
+    @Override
+    public Controller getController() {
+        NIAttachCustomizer panel = customizerRef.get();
+        if (panel != null) {
+            return panel.getController();
+        } else {
+            return null;
+        }
+    }
+
+}
diff --git a/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/api/NIDebugRunner.java b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/api/NIDebugRunner.java
new file mode 100644
index 0000000..cbb6770
--- /dev/null
+++ b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/api/NIDebugRunner.java
@@ -0,0 +1,90 @@
+/*
+ * 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.java.nativeimage.debugger.api;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+
+import org.netbeans.api.debugger.DebuggerEngine;
+import org.netbeans.api.extexecution.ExecutionDescriptor;
+import org.netbeans.api.project.Project;
+import org.netbeans.modules.nativeimage.api.debug.NIDebugger;
+import org.netbeans.modules.java.nativeimage.debugger.breakpoints.JPDABreakpointsHandler;
+import org.netbeans.modules.java.nativeimage.debugger.displayer.JavaFrameDisplayer;
+import org.netbeans.modules.java.nativeimage.debugger.displayer.JavaVariablesDisplayer;
+import static org.netbeans.spi.project.ActionProvider.COMMAND_DEBUG;
+
+/**
+ * Runs debugger with Java translations on a native image.
+ *
+ * @author martin
+ */
+public final class NIDebugRunner {
+
+    private NIDebugRunner() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Starts Native Image debugger.
+     *
+     * @param niFile Native Image file
+     * @param arguments a list of arguments when executing the native image
+     * @param debuggerCommand the debugger command
+     * @param project a project associated with the native image, or <code>null</code>
+     * @param displayName display name of the execution
+     * @param executionDescriptor execution descriptor
+     * @param startedEngine consumer of the started {@link DebuggerEngine}.
+     * @return an instance of {@link NIDebugger}.
+     * @throws IllegalStateException when the native debugger is not available.
+     */
+    public static NIDebugger start(File niFile, List<String> arguments, String debuggerCommand, Project project, String displayName, ExecutionDescriptor executionDescriptor, Consumer<DebuggerEngine> startedEngine) throws IllegalStateException {
+        JavaVariablesDisplayer variablesDisplayer = new JavaVariablesDisplayer();
+        JavaFrameDisplayer frameDisplayer = new JavaFrameDisplayer(project);
+        NIDebugger debugger = NIDebugger.newBuilder()
+                .frameDisplayer(frameDisplayer)
+                .variablesDisplayer(variablesDisplayer)
+                .build();
+        variablesDisplayer.setDebugger(debugger);
+        JPDABreakpointsHandler breakpointsHandler = new JPDABreakpointsHandler(niFile, debugger);
+        File workingDirectory = new File(System.getProperty("user.dir"));
+        List<String> command = arguments.isEmpty() ? Collections.singletonList(niFile.getAbsolutePath()) : join(niFile.getAbsolutePath(), arguments);
+        debugger.start(
+                command,
+                workingDirectory,
+                debuggerCommand,
+                COMMAND_DEBUG + " " + niFile.getName(),
+                executionDescriptor,
+                startedEngine).thenRun(() -> {
+                    breakpointsHandler.dispose();
+                });
+        return debugger;
+    }
+
+    private static List<String> join(String first, List<String> next) {
+        List<String> joined = new ArrayList<>(next.size() + 1);
+        joined.add(first);
+        joined.addAll(next);
+        return joined;
+    }
+
+}
diff --git a/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/breakpoints/JPDABreakpointsHandler.java b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/breakpoints/JPDABreakpointsHandler.java
new file mode 100644
index 0000000..2f43091
--- /dev/null
+++ b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/breakpoints/JPDABreakpointsHandler.java
@@ -0,0 +1,183 @@
+/*
+ * 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.java.nativeimage.debugger.breakpoints;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.HashSet;
+import java.util.Set;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import org.netbeans.api.debugger.Breakpoint;
+import org.netbeans.api.debugger.DebuggerManager;
+import org.netbeans.api.debugger.DebuggerManagerAdapter;
+import org.netbeans.api.debugger.jpda.JPDABreakpoint;
+import org.netbeans.api.debugger.jpda.LineBreakpoint;
+import org.netbeans.api.java.classpath.GlobalPathRegistry;
+import org.netbeans.modules.nativeimage.api.debug.NIDebugger;
+import org.netbeans.modules.nativeimage.api.debug.NILineBreakpointDescriptor;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.filesystems.URLMapper;
+
+/**
+ * Gathers JPDA breakpoints and submits them to the native debugger.
+ */
+public class JPDABreakpointsHandler extends DebuggerManagerAdapter implements PropertyChangeListener {
+
+    private final File niFileSources;
+    private final NIDebugger debugger;
+    private final Set<JPDABreakpoint> attachedBreakpoints = new HashSet<>();
+
+    public JPDABreakpointsHandler(File niFile, NIDebugger debugger) {
+        this.niFileSources = getNativeSources(niFile);
+        this.debugger = debugger;
+        DebuggerManager dm = DebuggerManager.getDebuggerManager();
+        dm.addDebuggerListener(DebuggerManager.PROP_BREAKPOINTS, this);
+        Breakpoint[] bs = dm.getBreakpoints();
+        for (Breakpoint b : bs) {
+            add(b);
+        }
+    }
+
+    private static File getNativeSources(File niFile) {
+        File sources = new File(niFile.getParentFile(), "sources");
+        if (sources.isDirectory()) {
+            return sources;
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void breakpointAdded(Breakpoint breakpoint) {
+        add(breakpoint);
+    }
+
+    @Override
+    public void breakpointRemoved(Breakpoint breakpoint) {
+        if (breakpoint instanceof JPDABreakpoint) {
+            breakpoint.removePropertyChangeListener(this);
+            debugger.removeBreakpoint(breakpoint);
+            synchronized (attachedBreakpoints) {
+                attachedBreakpoints.remove(breakpoint);
+            }
+        }
+    }
+
+    @Override
+    public void propertyChange(PropertyChangeEvent evt) {
+        Object source = evt.getSource();
+        String propertyName = evt.getPropertyName();
+        if (source instanceof JPDABreakpoint && !(Breakpoint.PROP_DISPOSED.equals(propertyName) || Breakpoint.PROP_VALIDITY.equals(propertyName))) {
+            // Change of breakpoint  properties
+            added((JPDABreakpoint) source);
+        }
+    }
+
+    private void add(Breakpoint b) {
+        if (b instanceof JPDABreakpoint && !((JPDABreakpoint) b).isHidden()) {
+            JPDABreakpoint jb = (JPDABreakpoint) b;
+            jb.addPropertyChangeListener(this);
+            synchronized (attachedBreakpoints) {
+                attachedBreakpoints.add(jb);
+            }
+            Breakpoint nativeBreakpoint = added(jb);
+            if (nativeBreakpoint != null) {
+                nativeBreakpoint.addPropertyChangeListener(Breakpoint.PROP_VALIDITY, e -> {
+                    Breakpoint.VALIDITY validity = nativeBreakpoint.getValidity();
+                    String validityMessage = nativeBreakpoint.getValidityMessage();
+                    ((ChangeListener) jb).stateChanged(new ValidityChanger(validity, validityMessage));
+                });
+            }
+        }
+    }
+
+    private Breakpoint added(JPDABreakpoint b) {
+        if (b instanceof LineBreakpoint) {
+            LineBreakpoint lb = (LineBreakpoint) b;
+            URL url;
+            try {
+                url = new URL(lb.getURL());
+            } catch (MalformedURLException ex) {
+                return null;
+            }
+            String filePath = null;
+            if (niFileSources != null) {
+                FileObject fo;
+                fo = URLMapper.findFileObject(url);
+                for (FileObject root : GlobalPathRegistry.getDefault().getSourceRoots()) {
+                    if (FileUtil.isParentOf(root, fo)) {
+                        String path = FileUtil.getRelativePath(root, fo);
+                        File sourcesFile = new File(niFileSources, path);
+                        filePath = sourcesFile.getAbsolutePath();
+                        break;
+                    }
+                }
+            }
+            if (filePath == null) {
+                try {
+                    filePath = new File(url.toURI()).getAbsolutePath();
+                } catch (URISyntaxException ex) {
+                    return null;
+                }
+            }
+            NILineBreakpointDescriptor niBreakpointDescriptor = NILineBreakpointDescriptor.newBuilder(filePath, lb.getLineNumber())
+                    .condition(lb.getCondition())
+                    .enabled(lb.isEnabled())
+                    .hidden(true)
+                    .build();
+            Object nativeBreakpoint = debugger.addLineBreakpoint(lb, niBreakpointDescriptor);
+            return (Breakpoint) nativeBreakpoint;
+        }
+        return null;
+    }
+
+    public void dispose() {
+        synchronized (attachedBreakpoints) {
+            for (JPDABreakpoint jb : attachedBreakpoints) {
+                jb.removePropertyChangeListener(this);
+                debugger.removeBreakpoint(jb);
+                ((ChangeListener) jb).stateChanged(new ValidityChanger(Breakpoint.VALIDITY.UNKNOWN, null));
+            }
+            attachedBreakpoints.clear();
+        }
+        DebuggerManager.getDebuggerManager().removeDebuggerListener(DebuggerManager.PROP_BREAKPOINTS, this);
+    }
+
+    private static class ValidityChanger extends ChangeEvent {
+
+        private final String validityMessage;
+
+        ValidityChanger(Breakpoint.VALIDITY validity, String validityMessage) {
+            super(validity);
+            this.validityMessage = validityMessage;
+        }
+
+        @Override
+        public String toString() {
+            return validityMessage;
+        }
+    }
+}
diff --git a/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/displayer/JavaFrameDisplayer.java b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/displayer/JavaFrameDisplayer.java
new file mode 100644
index 0000000..2123c98
--- /dev/null
+++ b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/displayer/JavaFrameDisplayer.java
@@ -0,0 +1,206 @@
+/*
+ * 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.java.nativeimage.debugger.displayer;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.classpath.GlobalPathRegistry;
+import org.netbeans.api.java.project.JavaProjectConstants;
+import org.netbeans.api.java.queries.SourceForBinaryQuery;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectUtils;
+import org.netbeans.api.project.SourceGroup;
+import org.netbeans.modules.nativeimage.api.debug.NIFrame;
+import org.netbeans.modules.nativeimage.spi.debug.filters.FrameDisplayer;
+import org.netbeans.spi.java.classpath.PathResourceImplementation;
+import org.netbeans.spi.java.classpath.support.ClassPathSupport;
+import org.openide.filesystems.FileObject;
+
+/**
+ * Display native frames like Java frames.
+ *
+ * @author martin
+ */
+public final class JavaFrameDisplayer implements FrameDisplayer {
+
+    private final ClassPath sourcePath;
+
+    public JavaFrameDisplayer(Project project) {
+        this.sourcePath = findSourcePath(project);
+    }
+
+    @Override
+    public DisplayedFrame displayed(NIFrame frame) {
+        String functionName = frame.getFunctionName();
+        if ("??".equals(functionName)) {    // NOI18N
+            return null;
+        }
+        return createJavaFrame(frame);
+    }
+
+    private DisplayedFrame createJavaFrame(NIFrame frame) {
+        return DisplayedFrame.newBuilder(getDisplayName(frame))
+                .description(getDescription(frame))
+                .line(frame.getLine())
+                .sourceURISupplier(() -> getSourceURI(frame))
+                .build();
+    }
+
+    private static String getDisplayName(NIFrame frame) {
+        String functionName = frame.getFunctionName();
+        String clsMethod;
+        int methodEnd = functionName.indexOf('(');
+        if (methodEnd < 0) {
+            methodEnd = functionName.length();
+        }
+        int methodStart = functionName.lastIndexOf('.', methodEnd);
+        if (methodStart < 0) {
+            clsMethod = functionName.substring(0, methodEnd);
+        } else {
+            int clsStart = functionName.lastIndexOf('.', methodStart - 1);
+            if (clsStart < 0) {
+                clsStart = 0;
+            } else {
+                clsStart++;
+            }
+            clsMethod = functionName.substring(clsStart, methodEnd);
+        }
+        int line = frame.getLine();
+        if (line < 0) {
+            return clsMethod;
+        } else {
+            return clsMethod + ':' + line;
+        }
+    }
+
+    private static String getDescription(NIFrame frame) {
+        String functionName = frame.getFunctionName();
+        int methodEnd = functionName.indexOf('(');
+        if (methodEnd < 0) {
+            methodEnd = functionName.length();
+        }
+        String clsMethod = functionName.substring(0, methodEnd);
+        int line = frame.getLine();
+        if (line < 0) {
+            return clsMethod;
+        } else {
+            return clsMethod + ':' + line;
+        }
+    }
+
+    private URI getSourceURI(NIFrame frame) {
+        String functionName = frame.getFunctionName();
+        int methodEnd = functionName.indexOf('(');
+        if (methodEnd > 0) {
+            int methodStart = functionName.lastIndexOf('.', methodEnd);
+            if (methodStart > 0) {
+                String className = functionName.substring(0, methodStart);
+                URI uri = findClassURI(sourcePath, className);
+                if (uri != null) {
+                    return uri;
+                }
+            }
+        }
+        String fullFileName = frame.getFullFileName();
+        if (fullFileName != null && !fullFileName.isEmpty()) {
+            return new File(fullFileName).toURI();
+        } else {
+            return null;
+        }
+    }
+
+    private static URI findClassURI(ClassPath sourcePath, String className) {
+        String sourceName = className;
+        int i = sourceName.indexOf ('$');
+        if (i > 0) {
+            sourceName = sourceName.substring (0, i);
+        }
+        sourceName = sourceName.replace('.', '/') + ".java";
+        FileObject resource;
+        if (sourcePath != null) {
+            resource = sourcePath.findResource(sourceName);
+        } else {
+            resource = GlobalPathRegistry.getDefault().findResource(sourceName);
+        }
+        if (resource != null) {
+            return resource.toURI();
+        } else {
+            return null;
+        }
+    }
+
+    private static ClassPath findSourcePath(Project project) {
+        if (project != null) {
+            List<FileObject> allSourceRoots = new ArrayList<>();
+            Set<FileObject> preferredRoots = new HashSet<>();
+            Set<FileObject> addedBinaryRoots = new HashSet<>();
+            SourceGroup[] sgs = ProjectUtils.getSources(project).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
+            for (SourceGroup sg : sgs) {
+                ClassPath ecp = ClassPath.getClassPath(sg.getRootFolder(), ClassPath.EXECUTE);
+                if (ecp == null) {
+                    ecp = ClassPath.getClassPath(sg.getRootFolder(), ClassPath.SOURCE);
+                }
+                if (ecp != null) {
+                    FileObject[] binaryRoots = ecp.getRoots();
+                    for (FileObject fo : binaryRoots) {
+                        if (addedBinaryRoots.contains(fo)) {
+                            continue;
+                        }
+                        addedBinaryRoots.add(fo);
+                        FileObject[] roots = SourceForBinaryQuery.findSourceRoots(fo.toURL()).getRoots();
+                        for (FileObject fr : roots) {
+                            if (!preferredRoots.contains(fr)) {
+                                allSourceRoots.add(fr);
+                                preferredRoots.add(fr);
+                            }
+                        }
+                    }
+                }
+            }
+            return createClassPath(allSourceRoots);
+        } else {
+            return null;
+        }
+    }
+
+    private static ClassPath createClassPath(Collection<FileObject> froots) {
+        List<PathResourceImplementation> pris = new ArrayList<> ();
+        for (FileObject fo : froots) {
+            if (fo != null && fo.canRead()) {
+                try {
+                    URL url = fo.toURL();
+                    pris.add(ClassPathSupport.createResource(url));
+                } catch (IllegalArgumentException iaex) {
+                    // Can be thrown from ClassPathSupport.createResource()
+                    // Ignore - bad source root
+                }
+            }
+        }
+        return ClassPathSupport.createClassPath(pris);
+    }
+
+}
diff --git a/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/displayer/JavaVariablesDisplayer.java b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/displayer/JavaVariablesDisplayer.java
new file mode 100644
index 0000000..191e2a9
--- /dev/null
+++ b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/displayer/JavaVariablesDisplayer.java
@@ -0,0 +1,596 @@
+/*
+ * 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.java.nativeimage.debugger.displayer;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.netbeans.modules.nativeimage.api.debug.EvaluateException;
+import org.netbeans.modules.nativeimage.api.debug.NIDebugger;
+import org.netbeans.modules.nativeimage.api.debug.NIFrame;
+import org.netbeans.modules.nativeimage.api.debug.NIVariable;
+import org.netbeans.modules.nativeimage.spi.debug.filters.VariableDisplayer;
+
+/**
+ *
+ * @author martin
+ */
+public final class JavaVariablesDisplayer implements VariableDisplayer {
+
+    private static final String HUB = "__hub__";
+    private static final String ARRAY = "__array__";
+    private static final String ARRAY_LENGTH = "__length__";
+    private static final String COMPRESSED_REF_REFIX = "_z_.";
+    private static final String PUBLIC = "public";
+    private static final String STRING_VALUE = "value";
+    private static final String HASH = "hash";
+    private static final String UNSET = "<optimized out>";
+
+    private static final String[] STRING_TYPES = new String[] { String.class.getName(), StringBuilder.class.getName(), StringBuffer.class.getName() };
+
+    private NIDebugger debugger;
+
+    public JavaVariablesDisplayer() {
+    }
+
+    public void setDebugger(NIDebugger debugger) {
+        this.debugger = debugger;
+    }
+
+    @Override
+    public NIVariable[] displayed(NIVariable[] variables) {
+        List<NIVariable> displayedVars = new ArrayList<>(variables.length);
+        for (NIVariable var : variables) {
+           String value = var.getValue();
+            if (UNSET.equals(value)) {
+                continue;
+            }
+            int nch = var.getNumChildren();
+            NIVariable displayedVar;
+            if (nch == 0) {
+                String name = var.getName();
+                if (!name.equals(getNameOrIndex(name))) {
+                    displayedVar = new Var(var);
+                } else {
+                    displayedVar = var;
+                }
+            } else {
+                NIVariable[] children = var.getChildren();
+                NIVariable[] subChildren = children[0].getChildren();
+                // Check for Array
+                if (subChildren.length == 3 &&
+                        //HUB.equals(subChildren[0].getName()) &&
+                        ARRAY_LENGTH.equals(subChildren[1].getName()) &&
+                        ARRAY.equals(subChildren[2].getName())) {
+                    displayedVar = new ArrayVar(var, subChildren[1], subChildren[2]);
+                } else {
+                    // Check for String
+                    String type = getSimpleType(var.getType());
+                    boolean isString = STRING_TYPES[0].equals(type);
+                    boolean likeString = isString;
+                    if (!likeString) {
+                        for (String strType : STRING_TYPES) {
+                            if (strType.equals(type)) {
+                                likeString = true;
+                                break;
+                            }
+                        }
+                    }
+                    if (likeString) {
+                        displayedVar = new StringVar(var, type, isString ? null : subChildren);
+                    } else {
+                        if (children.length == 1 && PUBLIC.equals(children[0].getName())) {
+                            // Object children
+                            displayedVar = new ObjectVar(var, subChildren);
+                        } else {
+                            String name = var.getName();
+                            if (!name.equals(getNameOrIndex(name))) {
+                                displayedVar = new Var(var);
+                            } else {
+                                displayedVar = var;
+                            }
+                        }
+                    }
+                }
+            }
+            displayedVars.add(displayedVar);
+        }
+        return displayedVars.toArray(new NIVariable[displayedVars.size()]);
+    }
+
+    private static String displayType(String type) {
+        if (type.startsWith(COMPRESSED_REF_REFIX)) {
+            type = type.substring(COMPRESSED_REF_REFIX.length());
+        }
+        if (type.endsWith("*")) {
+            type = type.substring(0, type.length() - 1).trim();
+        }
+        return type;
+    }
+
+    private static String getSimpleType(String type) {
+        type = displayType(type);
+        for (int i = 0; i < type.length(); i++) {
+            char c = type.charAt(i);
+            if (c != '.' && !Character.isJavaIdentifierPart(c)) {
+                return type.substring(0, i);
+            }
+        }
+        return type;
+    }
+
+    private static boolean isOfType(String varType, String type) {
+        varType = displayType(varType);
+        return varType.equals(type) || varType.startsWith(type) && !Character.isJavaIdentifierPart(varType.charAt(type.length()));
+    }
+
+    private static int getTypeSize(String type) {
+        type = getSimpleType(type);
+        switch (type) {
+            case "boolean":
+            case "byte":
+                return 1;
+            case "char":
+            case "short":
+                return 2;
+            case "int":
+            case "float":
+                return 4;
+            case "long":
+            case "double":
+                return 8;
+            default:
+                return 8;
+        }
+    }
+
+    private static Map<String, NIVariable> getVarsByName(NIVariable[] vars) {
+        switch (vars.length) {
+            case 0:
+                return Collections.emptyMap();
+            case 1:
+                return Collections.singletonMap(vars[0].getName(), vars[0]);
+            default:
+                Map<String, NIVariable> varsByName = new HashMap<>(vars.length);
+                for (NIVariable var : vars) {
+                    varsByName.put(var.getName(), var);
+                }
+                return varsByName;
+        }
+    }
+
+    private static NIVariable[] restrictChildren(NIVariable[] children, int from, int to) {
+        if (from > 0 || to < children.length) {
+            to = Math.min(to, children.length);
+            if (from < to) {
+                children = Arrays.copyOfRange(children, from, to);
+            } else {
+                children = new NIVariable[]{};
+            }
+        }
+        return children;
+    }
+
+    private static String getHash(NIVariable[] children) {
+        for (NIVariable child : children) {
+            if (HASH.equals(child.getName())) {
+                String hash = child.getValue();
+                try {
+                    hash = Integer.toHexString(Integer.parseInt(hash));
+                } catch (NumberFormatException ex) {}
+                return hash;
+            }
+        }
+        return null;
+    }
+
+    private static String getArrayExpression(NIVariable variable) {
+        StringBuilder arrayExpression = new StringBuilder(variable.getName());
+        while ((variable = variable.getParent()) != null) {
+            if (!PUBLIC.equals(variable.getName())) {
+                arrayExpression.insert(0, '.');
+                arrayExpression.insert(0, variable.getName());
+            }
+        }
+        return arrayExpression.toString();
+    }
+
+    private static String getNameOrIndex(String name) {
+        if (name.endsWith("]")) {
+            int i = name.lastIndexOf(ARRAY+"[");
+            if (i > 0) {
+                String index = name.substring(i + ARRAY.length() + 1, name.length() - 1);
+                return index;
+            }
+        }
+        return name;
+    }
+
+    private String readArray(NIVariable lengthVariable, int itemSize) {
+        int length = Integer.parseInt(lengthVariable.getValue());
+        String expressionPath = lengthVariable.getExpressionPath();
+        if (expressionPath != null && !expressionPath.isEmpty()) {
+            String addressExpr = "&" + expressionPath;
+            return debugger.readMemory(addressExpr, 4, length * itemSize); // length has 4 bytes
+        }
+        return null;
+    }
+
+    private static NIVariable[] getObjectChildren(NIVariable[] children, int from, int to) {
+        for (int i = 0; i < children.length; i++) {
+            if (HUB.equals(children[i].getName())) {
+                NIVariable[] ch1 = Arrays.copyOf(children, i);
+                NIVariable[] ch2 = Arrays.copyOfRange(children, i + 1, children.length);
+                if (ch1.length == 0) {
+                    return ch2;
+                }
+                if (ch2.length == 0) {
+                    return ch1;
+                }
+                NIVariable[] ch = new NIVariable[ch1.length + ch2.length];
+                System.arraycopy(ch1, 0, ch, 0, ch1.length);
+                System.arraycopy(ch2, 0, ch, ch1.length, ch2.length);
+                return ch;
+            }
+        }
+        return restrictChildren(children, from, to);
+    }
+
+    private class StringVar implements NIVariable {
+
+        private final NIVariable var;
+        private final String type;
+        private final NIVariable[] children;
+
+        StringVar(NIVariable var, String type, NIVariable[] children) {
+            this.var = var;
+            this.type = type;
+            this.children = children;
+        }
+
+        @Override
+        public NIFrame getFrame() {
+            return var.getFrame();
+        }
+
+        @Override
+        public String getName() {
+            return getNameOrIndex(var.getName());
+        }
+
+        @Override
+        public String getType() {
+            return type;
+        }
+
+        @Override
+        public String getValue() {
+            NIVariable pub = getVarsByName(var.getChildren()).get(PUBLIC);
+            Map<String, NIVariable> arrayInfo = getVarsByName(getVarsByName(pub.getChildren()).get(STRING_VALUE).getChildren());
+            arrayInfo = getVarsByName(arrayInfo.get(PUBLIC).getChildren());
+            NIVariable arrayVariable = arrayInfo.get(ARRAY);
+            NIVariable lengthVariable = arrayInfo.get(ARRAY_LENGTH);
+            String hexArray = readArray(lengthVariable, 2);
+            if (hexArray != null) {
+                return parseUTF16(hexArray);
+            } else { // legacy code
+                String arrayExpression = getArrayExpression(arrayVariable);
+                int length = Integer.parseInt(lengthVariable.getValue());
+                char[] characters = new char[length];
+                try {
+                    for (int i = 0; i < length; i++) {
+                        NIVariable charVar = debugger.evaluate(arrayExpression + "[" + i + "]", null, var.getFrame());
+                        characters[i] = charVar.getValue().charAt(1);
+                    }
+                } catch (EvaluateException ex) {
+                    return ex.getLocalizedMessage();
+                }
+                return new String(characters);
+            }
+        }
+
+        private String parseUTF16(String hexArray) {
+            CharsetDecoder cd = Charset.forName("utf-16").newDecoder();
+            ByteBuffer buffer = ByteBuffer.allocate(2);
+            int length = hexArray.length() / 4;
+            char[] characters = new char[length];
+            int ih = 0;
+            for (int i = 0; i < length; i++) {
+                byte b1 = parseByte(hexArray, ih);
+                ih += 2;
+                byte b0 = parseByte(hexArray, ih);
+                ih += 2;
+                buffer.rewind();
+                buffer.put(b0);
+                buffer.put(b1);
+                buffer.rewind();
+                try {
+                    char c = cd.decode(buffer).get();
+                    characters[i] = c;
+                } catch (CharacterCodingException ex) {
+                }
+            }
+            return new String(characters);
+        }
+
+        private byte parseByte(String hexArray, int offset) {
+            String hex = new String(new char[] {hexArray.charAt(offset), hexArray.charAt(offset + 1)});
+            return (byte) (Integer.parseInt(hex, 16) & 0xFF);
+        }
+
+        @Override
+        public int getNumChildren() {
+            return children != null ? children.length : 0;
+        }
+
+        @Override
+        public NIVariable[] getChildren(int from, int to) {
+            return children != null ? getObjectChildren(children, from, to) : new NIVariable[]{};
+        }
+
+        @Override
+        public NIVariable getParent() {
+            return var.getParent();
+        }
+
+        @Override
+        public String getExpressionPath() {
+            return var.getExpressionPath();
+        }
+    }
+
+    private class ArrayVar implements NIVariable {
+
+        private final NIVariable var;
+        private final NIVariable lengthVariable;
+        private final int length;
+        private final NIVariable array;
+
+        ArrayVar(NIVariable var, NIVariable lengthVariable, NIVariable array) {
+            this.var = var;
+            this.lengthVariable = lengthVariable;
+            int arrayLength;
+            try {
+                arrayLength = Integer.parseInt(lengthVariable.getValue());
+            } catch (NumberFormatException ex) {
+                arrayLength = 0;
+            }
+            this.length = arrayLength;
+            this.array = array;
+        }
+
+        @Override
+        public NIFrame getFrame() {
+            return var.getFrame();
+        }
+
+        @Override
+        public NIVariable getParent() {
+            return var.getParent();
+        }
+
+        @Override
+        public String getName() {
+            return getNameOrIndex(var.getName());
+        }
+
+        @Override
+        public String getType() {
+            return displayType(var.getType());
+        }
+
+        @Override
+        public String getValue() {
+            String value = var.getValue();
+            if (value.startsWith("@")) {
+                value = getType() + value;
+            }
+            return value + "(length="+length+")";
+        }
+
+        @Override
+        public int getNumChildren() {
+            return length;
+        }
+
+        @Override
+        public NIVariable[] getChildren(int from, int to) {
+            if (from >= 0) {
+                to = Math.min(to, length);
+            } else {
+                from = 0;
+                to = length;
+            }
+            if (from >= to) {
+                return new NIVariable[]{};
+            }
+
+            String arrayAddress = null;
+            String expressionPath = lengthVariable.getExpressionPath();
+            if (expressionPath != null && !expressionPath.isEmpty()) {
+                String addressExpr = "&" + expressionPath;
+                NIVariable addressVariable;
+                try {
+                    addressVariable = debugger.evaluate(addressExpr, null, lengthVariable.getFrame());
+                } catch (EvaluateException ex) {
+                    addressVariable = null;
+                }
+                if (addressVariable != null) {
+                    String address = addressVariable.getValue();
+                    address = address.toLowerCase();
+                    if (address.startsWith("0x")) {
+                        arrayAddress = address;
+                    }
+                }
+            }
+            NIVariable[] elements = new NIVariable[to - from];
+            try {
+                if (arrayAddress != null) {
+                    String itemExpression = "*(" + getSimpleType(getType()) + "*)(" + arrayAddress + "+";
+                    int size = getTypeSize(getType());
+                    int offset = 4 + from*size;
+                    for (int i = from; i < to; i++) {
+                        NIVariable element = debugger.evaluate(itemExpression + offset + ")", Integer.toString(i), var.getFrame());
+                        offset += size;
+                        elements[i - from] = element;
+                    }
+                } else {
+                    String arrayExpression = getArrayExpression(array);
+                    for (int i = from; i < to; i++) {
+                        NIVariable element = debugger.evaluate(arrayExpression + "[" + i + "]", Integer.toString(i), var.getFrame());
+                        elements[i - from] = element;
+                    }
+                }
+            } catch (EvaluateException ex) {
+                return new NIVariable[]{};
+            }
+            return elements;
+        }
+
+        @Override
+        public String getExpressionPath() {
+            return var.getExpressionPath();
+        }
+    }
+
+    private class ObjectVar implements NIVariable {
+
+        private final NIVariable var;
+        private final NIVariable[] children;
+
+        ObjectVar(NIVariable var, NIVariable[] children) {
+            this.var = var;
+            this.children = children;
+        }
+
+        @Override
+        public NIFrame getFrame() {
+            return var.getFrame();
+        }
+
+        @Override
+        public NIVariable getParent() {
+            return var.getParent();
+        }
+
+        @Override
+        public String getName() {
+            return getNameOrIndex(var.getName());
+        }
+
+        @Override
+        public String getType() {
+            return displayType(var.getType());
+        }
+
+        @Override
+        public String getValue() {
+            String value = var.getValue();
+            if (value.startsWith("@") || value.startsWith("0x")) {
+                String hash = getHash(children);
+                if (hash == null) {
+                    if (value.startsWith("@")) {
+                        hash = value.substring(1);
+                    } else {
+                        hash = value.substring(2);
+                    }
+                }
+                value = getType() + '@' + hash;
+            }
+            return value;
+        }
+
+        @Override
+        public int getNumChildren() {
+            return children.length;
+        }
+
+        @Override
+        public NIVariable[] getChildren(int from, int to) {
+            return getObjectChildren(children, from, to);
+        }
+
+        @Override
+        public String getExpressionPath() {
+            return var.getExpressionPath();
+        }
+    }
+
+    private class Var implements NIVariable {
+
+        private final NIVariable var;
+
+        Var(NIVariable var) {
+            this.var = var;
+        }
+
+        @Override
+        public NIFrame getFrame() {
+            return var.getFrame();
+        }
+
+        @Override
+        public NIVariable getParent() {
+            return var.getParent();
+        }
+
+        @Override
+        public String getName() {
+            return getNameOrIndex(var.getName());
+        }
+
+        @Override
+        public String getType() {
+            return var.getType();
+        }
+
+        @Override
+        public String getValue() {
+            return var.getValue();
+        }
+
+        @Override
+        public int getNumChildren() {
+            return var.getNumChildren();
+        }
+
+        @Override
+        public NIVariable[] getChildren(int from, int to) {
+            return var.getChildren(from, to);
+        }
+
+        @Override
+        public NIVariable[] getChildren() {
+            return var.getChildren();
+        }
+
+        @Override
+        public String getExpressionPath() {
+            return var.getExpressionPath();
+        }
+    }
+}
diff --git a/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/resources/mf-layer.xml b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/resources/mf-layer.xml
new file mode 100644
index 0000000..73421fc
--- /dev/null
+++ b/java/java.nativeimage.debugger/src/org/netbeans/modules/java/nativeimage/debugger/resources/mf-layer.xml
@@ -0,0 +1,32 @@
+<?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 filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.0//EN" "http://www.netbeans.org/dtds/filesystem-1_0.dtd">
+<filesystem>
+
+    <folder name="Editors">
+        <folder name="text">
+        </folder>
+        <folder name="AnnotationTypes">
+        </folder>
+    </folder>
+
+</filesystem>
diff --git a/java/java.nativeimage.debugger/test/unit/src/org/netbeans/modules/java/nativeimage/debugger/NIDebugRunnerTest.java b/java/java.nativeimage.debugger/test/unit/src/org/netbeans/modules/java/nativeimage/debugger/NIDebugRunnerTest.java
new file mode 100644
index 0000000..7ed9276
--- /dev/null
+++ b/java/java.nativeimage.debugger/test/unit/src/org/netbeans/modules/java/nativeimage/debugger/NIDebugRunnerTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.java.nativeimage.debugger;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collections;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+import org.netbeans.api.debugger.DebuggerManager;
+import org.netbeans.api.debugger.jpda.LineBreakpoint;
+import org.netbeans.modules.java.nativeimage.debugger.api.NIDebugRunner;
+import org.netbeans.modules.nativeimage.api.debug.EvaluateException;
+import org.netbeans.modules.nativeimage.api.debug.NIDebugger;
+import org.netbeans.modules.nativeimage.api.debug.NIVariable;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.Lookups;
+import org.openide.util.lookup.ProxyLookup;
+
+/**
+ *
+ * @author martin
+ */
+public class NIDebugRunnerTest {
+
+    public NIDebugRunnerTest() {
+    }
+
+    private static Lookup getTestLookup() {
+        Lookup launchCtx = new ProxyLookup(
+                        Lookups.fixed(new TestNIDebuggerServiceProvider()),
+                        Lookup.getDefault()
+                );
+        return launchCtx;
+    }
+
+    @Test
+    public void testDebuggerProviderBreakpoints() {
+        LineBreakpoint bp1 = LineBreakpoint.create("file:///testFile", 10);
+        DebuggerManager.getDebuggerManager().addBreakpoint(bp1);
+        Lookups.executeWith(getTestLookup(), () -> {
+            NIDebugger debugger = NIDebugRunner.start(new File("NIFile"), Arrays.asList("ARG1", "ARG2"), "MI", null, "displayName", null, engine -> {});
+            try {
+                NIVariable result = debugger.evaluate("breakpoints", null, null);
+                assertEquals(1, result.getChildren().length);
+                assertEquals("/testFile:10", result.getChildren()[0].getValue());
+            } catch (EvaluateException ex) {
+                throw new AssertionError(ex.getLocalizedMessage(), ex);
+            }
+        });
+        DebuggerManager.getDebuggerManager().removeBreakpoint(bp1);
+    }
+
+    @Test
+    public void testVersion() {
+        Lookups.executeWith(getTestLookup(), () -> {
+            NIDebugger debugger = NIDebugRunner.start(new File("NIFile"), Collections.emptyList(), "MI", null, "displayName", null, engine -> {});
+            assertEquals("Test1", debugger.getVersion());
+        });
+    }
+}
diff --git a/java/java.nativeimage.debugger/test/unit/src/org/netbeans/modules/java/nativeimage/debugger/TestNIDebuggerProvider.java b/java/java.nativeimage.debugger/test/unit/src/org/netbeans/modules/java/nativeimage/debugger/TestNIDebuggerProvider.java
new file mode 100644
index 0000000..a514ddf
--- /dev/null
+++ b/java/java.nativeimage.debugger/test/unit/src/org/netbeans/modules/java/nativeimage/debugger/TestNIDebuggerProvider.java
@@ -0,0 +1,122 @@
+/*
+ * 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.java.nativeimage.debugger;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+import org.netbeans.api.debugger.Breakpoint;
+import org.netbeans.api.debugger.DebuggerEngine;
+
+import org.netbeans.api.extexecution.ExecutionDescriptor;
+import org.netbeans.modules.nativeimage.api.debug.NIFrame;
+import org.netbeans.modules.nativeimage.api.debug.NILineBreakpointDescriptor;
+import org.netbeans.modules.nativeimage.api.debug.NIVariable;
+import org.netbeans.modules.nativeimage.spi.debug.NIDebuggerProvider;
+import org.netbeans.modules.nativeimage.spi.debug.filters.FrameDisplayer;
+import org.netbeans.modules.nativeimage.spi.debug.filters.VariableDisplayer;
+
+public class TestNIDebuggerProvider implements NIDebuggerProvider {
+
+    private final Map<Object, Breakpoint> breakpoints = new LinkedHashMap<>();
+    private FrameDisplayer frameDisplayer;
+    private VariableDisplayer variablesDisplayer;
+
+    public TestNIDebuggerProvider() {
+    }
+
+    @Override
+    public Breakpoint addLineBreakpoint(Object id, NILineBreakpointDescriptor breakpointDescriptor) {
+        Breakpoint nativeBreakpoint = new Breakpoint() {
+            @Override
+            public boolean isEnabled() {
+                return breakpointDescriptor.isEnabled();
+            }
+
+            @Override
+            public void disable() {
+                throw new UnsupportedOperationException("Not supported yet.");
+            }
+
+            @Override
+            public void enable() {
+                throw new UnsupportedOperationException("Not supported yet.");
+            }
+
+            @Override
+            public String toString() {
+                return breakpointDescriptor.getFilePath() + ':' + breakpointDescriptor.getLine();
+            }
+        };
+        breakpoints.put(id, nativeBreakpoint);
+        return nativeBreakpoint;
+    }
+
+    @Override
+    public void removeBreakpoint(Object id) {
+        breakpoints.remove(id);
+    }
+
+    @Override
+    public void setFrameDisplayer(FrameDisplayer frameDisplayer) {
+        this.frameDisplayer = frameDisplayer;
+    }
+
+    @Override
+    public void setVariablesDisplayer(VariableDisplayer variablesDisplayer) {
+        this.variablesDisplayer = variablesDisplayer;
+    }
+
+    @Override
+    public CompletableFuture<Void> start(List<String> command, File workingDirectory, String debugger, String displayName, ExecutionDescriptor executionDescriptor, Consumer<DebuggerEngine> startedEngine) {
+        return CompletableFuture.completedFuture(null);
+    }
+
+    @Override
+    public CompletableFuture<NIVariable> evaluateAsync(String expression, String resultName, NIFrame frame) {
+        NIVariable result;
+        if ("breakpoints".equals(expression)) {
+            NIVariable[] children = new NIVariable[breakpoints.size()];
+            result = new TestNIVariable(expression, "BP", "BP", null, children, null);
+            int i = 0;
+            for (Breakpoint b : breakpoints.values()) {
+                children[i++] = new TestNIVariable("b" + i, "BP", b.toString(), result, new NIVariable[]{}, null);
+            }
+        } else {
+            result = new TestNIVariable(expression, "type", "value", null, new NIVariable[]{}, null);
+        }
+        result = variablesDisplayer.displayed(result)[0];
+        return CompletableFuture.completedFuture(result);
+    }
+
+    @Override
+    public String readMemory(String address, long offset, int length) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public String getVersion() {
+        return "Test1";
+    }
+
+}
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/EvaluateException.java b/java/java.nativeimage.debugger/test/unit/src/org/netbeans/modules/java/nativeimage/debugger/TestNIDebuggerServiceProvider.java
similarity index 67%
rename from cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/EvaluateException.java
rename to java/java.nativeimage.debugger/test/unit/src/org/netbeans/modules/java/nativeimage/debugger/TestNIDebuggerServiceProvider.java
index 0fc9c76..7735bae 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/EvaluateException.java
+++ b/java/java.nativeimage.debugger/test/unit/src/org/netbeans/modules/java/nativeimage/debugger/TestNIDebuggerServiceProvider.java
@@ -16,17 +16,15 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.netbeans.modules.cpplite.debugger;
+package org.netbeans.modules.java.nativeimage.debugger;
 
-public final class EvaluateException extends Exception {
+import org.netbeans.modules.nativeimage.spi.debug.NIDebuggerProvider;
+import org.netbeans.modules.nativeimage.spi.debug.NIDebuggerServiceProvider;
 
-    /**
-     * Constructs an instance of <code>EvaluateException</code> with the
-     * specified detail message.
-     *
-     * @param msg the detail message.
-     */
-    EvaluateException(String msg) {
-        super(msg);
+public class TestNIDebuggerServiceProvider implements NIDebuggerServiceProvider {
+
+    @Override
+    public NIDebuggerProvider create() {
+        return new TestNIDebuggerProvider();
     }
 }
diff --git a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPVariable.java b/java/java.nativeimage.debugger/test/unit/src/org/netbeans/modules/java/nativeimage/debugger/TestNIVariable.java
similarity index 50%
copy from cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPVariable.java
copy to java/java.nativeimage.debugger/test/unit/src/org/netbeans/modules/java/nativeimage/debugger/TestNIVariable.java
index cd5c6fe..de5bde1 100644
--- a/cpplite/cpplite.debugger/src/org/netbeans/modules/cpplite/debugger/CPPVariable.java
+++ b/java/java.nativeimage.debugger/test/unit/src/org/netbeans/modules/java/nativeimage/debugger/TestNIVariable.java
@@ -16,66 +16,67 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.netbeans.modules.cpplite.debugger;
+package org.netbeans.modules.java.nativeimage.debugger;
 
-import java.util.Map;
-import java.util.Objects;
+import org.netbeans.modules.nativeimage.api.debug.NIFrame;
+import org.netbeans.modules.nativeimage.api.debug.NIVariable;
 
-import org.netbeans.modules.cnd.debugger.gdb2.mi.MIConst;
-import org.netbeans.modules.cnd.debugger.gdb2.mi.MIValue;
+public class TestNIVariable implements NIVariable {
 
-/**
- * Representation of a variable.
- */
-public final class CPPVariable {
-
-    private final CPPFrame frame;
-    private final String uniqueName;
     private final String name;
     private final String type;
     private final String value;
-    private final int numChildren;
-    private volatile Map<String, CPPVariable> children;
+    private final NIVariable parent;
+    private final NIVariable[] children;
+    private final NIFrame frame;
 
-    CPPVariable(CPPFrame frame, String uniqueName, String name, String type, MIValue value, int numChildren) {
-        this.frame = frame;
-        this.uniqueName = uniqueName;
+    TestNIVariable(String name, String type, String value, NIVariable parent, NIVariable[] children, NIFrame frame) {
         this.name = name;
         this.type = type;
-        this.value = (value instanceof MIConst) ? ((MIConst) value).value() : Objects.toString(value);
-        this.numChildren = numChildren;
-    }
-
-    public String getUniqueName() {
-        return uniqueName;
+        this.value = value;
+        this.parent = parent;
+        this.children = children;
+        this.frame = frame;
     }
 
+    @Override
     public String getName() {
         return name;
     }
 
+    @Override
     public String getType() {
         return type;
     }
 
+    @Override
     public String getValue() {
         return value;
     }
 
+    @Override
+    public NIVariable getParent() {
+        return parent;
+    }
+
+    @Override
     public int getNumChildren() {
-        return numChildren;
+        return children.length;
+    }
+
+    @Override
+    public NIVariable[] getChildren(int from, int to) {
+        return children;
     }
 
-    public Map<String, CPPVariable> getChildrenVariables() {
-        Map<String, CPPVariable> vars = children;
-        if (vars == null) {
-            synchronized (this) {
-                vars = children;
-                if (vars == null) {
-                    children = vars = CPPFrame.retrieveVariables(frame, this);
-                }
-            }
-        }
-        return vars;
+    @Override
+    public NIFrame getFrame() {
+        return frame;
     }
+
+    @Override
+    public String getExpressionPath() {
+        return "";
+    }
+
 }
diff --git a/nbbuild/build.properties b/nbbuild/build.properties
index fc358a4..759f93a 100644
--- a/nbbuild/build.properties
+++ b/nbbuild/build.properties
@@ -211,6 +211,7 @@ config.javadoc.friend=\
     java.j2seproject,\
     junit,\
     lib.v8debug,\
+    nativeimage.api,\
     versioning.core,\
     masterfs,\
     masterfs.ui,\
diff --git a/nbbuild/cluster.properties b/nbbuild/cluster.properties
index 4d447b1..7a93cd4 100644
--- a/nbbuild/cluster.properties
+++ b/nbbuild/cluster.properties
@@ -454,6 +454,7 @@ nb.cluster.ide=\
         lsp.client,\
         mercurial,\
         mylyn.util,\
+        nativeimage.api,\
         notifications,\
         o.apache.commons.httpclient,\
         o.apache.commons.lang,\
@@ -663,6 +664,7 @@ nb.cluster.java=\
         java.metrics,\
         java.module.graph,\
         java.mx.project,\
+        java.nativeimage.debugger,\
         java.navigation,\
         java.openjdk.project,\
         java.platform,\
diff --git a/nbbuild/javadoctools/links.xml b/nbbuild/javadoctools/links.xml
index 34a518f..5d07aaa 100644
--- a/nbbuild/javadoctools/links.xml
+++ b/nbbuild/javadoctools/links.xml
@@ -240,3 +240,4 @@
 <link href="${javadoc.docs.org-netbeans-modules-java-editor-lib}" offline="true" packagelistloc="${netbeans.javadoc.dir}/org-netbeans-modules-java-editor-lib"/>
 <link href="${javadoc.docs.org-netbeans-modules-web-common}" offline="true" packagelistloc="${netbeans.javadoc.dir}/org-netbeans-modules-web-common"/>
 <link href="${javadoc.docs.org-netbeans-modules-db-core}" offline="true" packagelistloc="${netbeans.javadoc.dir}/org-netbeans-modules-db-core"/>
+<link href="${javadoc.docs.org-netbeans-modules-nativeimage-api}" offline="true" packagelistloc="${netbeans.javadoc.dir}/org-netbeans-modules-nativeimage-api"/>
diff --git a/nbbuild/javadoctools/properties.xml b/nbbuild/javadoctools/properties.xml
index 6f1abb8..3c633d1 100644
--- a/nbbuild/javadoctools/properties.xml
+++ b/nbbuild/javadoctools/properties.xml
@@ -237,3 +237,4 @@
 <property name="javadoc.docs.org-netbeans-modules-java-editor-lib" value="${javadoc.web.root}/org-netbeans-modules-java-editor-lib"/>
 <property name="javadoc.docs.org-netbeans-modules-web-common" value="${javadoc.web.root}/org-netbeans-modules-web-common"/>
 <property name="javadoc.docs.org-netbeans-modules-db-core" value="${javadoc.web.root}/org-netbeans-modules-db-core"/>
+<property name="javadoc.docs.org-netbeans-modules-nativeimage-api" value="${javadoc.web.root}/org-netbeans-modules-nativeimage-api"/>
diff --git a/nbbuild/javadoctools/replaces.xml b/nbbuild/javadoctools/replaces.xml
index ce34768..f7e27e8 100644
--- a/nbbuild/javadoctools/replaces.xml
+++ b/nbbuild/javadoctools/replaces.xml
@@ -237,3 +237,4 @@
 <replacefilter token="@org-netbeans-modules-java-editor-lib@" value="${javadoc.docs.org-netbeans-modules-java-editor-lib}"/>
 <replacefilter token="@org-netbeans-modules-web-common@" value="${javadoc.docs.org-netbeans-modules-web-common}"/>
 <replacefilter token="@org-netbeans-modules-db-core@" value="${javadoc.docs.org-netbeans-modules-db-core}"/>
+<replacefilter token="@org-netbeans-modules-nativeimage-api@" value="${javadoc.docs.org-netbeans-modules-nativeimage-api}"/>

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

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


[netbeans] 01/03: Report closed InputOutputProvider. Not doing so cause problems on repeated write.

Posted by en...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit e4a9fb4d51a1b082ee834ba3e3d46a2da543b3eb
Author: Martin Entlicher <ma...@oracle.com>
AuthorDate: Mon Mar 22 14:51:37 2021 +0100

    Report closed InputOutputProvider. Not doing so cause problems on repeated write.
---
 .../modules/java/lsp/server/ui/AbstractLspInputOutputProvider.java    | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/AbstractLspInputOutputProvider.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/AbstractLspInputOutputProvider.java
index 593b602..ebc15ee 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/AbstractLspInputOutputProvider.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/AbstractLspInputOutputProvider.java
@@ -92,7 +92,7 @@ public abstract class AbstractLspInputOutputProvider implements InputOutputProvi
 
     @Override
     public final boolean isIOClosed(LspIO io) {
-        return false;
+        return io.closed;
     }
 
     @Override
@@ -133,6 +133,7 @@ public abstract class AbstractLspInputOutputProvider implements InputOutputProvi
         final Reader in;
         final PrintWriter out;
         final PrintWriter err;
+        volatile boolean closed;
 
         LspIO(String name, IOContext ioCtx, Lookup lookup) {
             this.name = name;
@@ -170,6 +171,7 @@ public abstract class AbstractLspInputOutputProvider implements InputOutputProvi
 
             @Override
             public void close() throws IOException {
+                closed = true;
             }
         }
     }

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

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