d-millar updated this revision to Diff 386169.
d-millar added a comment.

OK, well, took my some time but I have made an attempt to address the three 
areas described by Mr. Ingham:

//Support for a scripting language in lldb has three distinct pieces.

1. Making the SB API's available to the scripting languages
2. Adding all the callbacks so the scripting language can be used for 
breakpoint callbacks, data formatters, etc.
3. Adding a REPL for that language to the "script" command

//
With regard to #3, I had hoped JShell would work as a viable REPL.  
Unfortunately, it does not appear to support System.loadLibrary, which makes it 
somewhat useless in the current context.  Because I cannot load the JNI 
libraries, I would not have access to the classes in the Scripting Bridge, 
which sort of defeats the purpose.

As an alternative, I have implemented a quasi-REPL, which allows you to compose 
classes, compile, and run them.  The "script" command behaves for the most part 
like it would for Python or Lua, but have a set of /-prefixed commands, 
described in the initial message, that enable the extra functionality.  The 
JShell interface is also include via those commands - perhaps loadLibrary will 
be supported in the future.

Anyway, let me know what you think and what else you might need for possible 
inclusion of this patch.

Best,
Dave


Repository:
  rLLDB LLDB

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D111409/new/

https://reviews.llvm.org/D111409

Files:
  lldb/CMakeLists.txt
  lldb/bindings/CMakeLists.txt
  lldb/bindings/java/CMakeLists.txt
  lldb/bindings/java/SWIG/LldbScriptInterpreter.java
  lldb/bindings/java/java-swigsafecast.swig
  lldb/bindings/java/java-typemaps.swig
  lldb/bindings/java/java-wrapper.swig
  lldb/bindings/java/java.swig
  lldb/cmake/modules/FindJavaAndSwig.cmake
  lldb/cmake/modules/LLDBConfig.cmake
  lldb/docs/resources/build.rst
  lldb/include/lldb/Core/IOHandler.h
  lldb/include/lldb/Host/Config.h.cmake
  lldb/include/lldb/lldb-enumerations.h
  lldb/source/API/CMakeLists.txt
  lldb/source/API/SBDebugger.cpp
  lldb/source/API/liblldb-private.exports
  lldb/source/API/liblldb.exports
  lldb/source/Commands/CommandObjectBreakpointCommand.cpp
  lldb/source/Commands/CommandObjectScript.cpp
  lldb/source/Commands/CommandObjectWatchpointCommand.cpp
  lldb/source/Interpreter/OptionArgParser.cpp
  lldb/source/Interpreter/ScriptInterpreter.cpp
  lldb/source/Plugins/ScriptInterpreter/CMakeLists.txt
  lldb/source/Plugins/ScriptInterpreter/Java/CMakeLists.txt
  lldb/source/Plugins/ScriptInterpreter/Java/Java.cpp
  lldb/source/Plugins/ScriptInterpreter/Java/Java.h
  lldb/source/Plugins/ScriptInterpreter/Java/ScriptInterpreterJava.cpp
  lldb/source/Plugins/ScriptInterpreter/Java/ScriptInterpreterJava.h
  lldb/test/CMakeLists.txt
  lldb/test/Shell/ScriptInterpreter/Java/Inputs/independent_state.in
  lldb/test/Shell/ScriptInterpreter/Java/Inputs/nested_sessions.in
  lldb/test/Shell/ScriptInterpreter/Java/Inputs/nested_sessions_2.in
  lldb/test/Shell/ScriptInterpreter/Java/Inputs/testmodule.java
  lldb/test/Shell/ScriptInterpreter/Java/bindings.test
  lldb/test/Shell/ScriptInterpreter/Java/breakpoint_callback.test
  lldb/test/Shell/ScriptInterpreter/Java/breakpoint_function_callback.test
  lldb/test/Shell/ScriptInterpreter/Java/breakpoint_oneline_callback.test
  lldb/test/Shell/ScriptInterpreter/Java/command_script_import.test
  lldb/test/Shell/ScriptInterpreter/Java/convenience_variables.test
  lldb/test/Shell/ScriptInterpreter/Java/fail_breakpoint_oneline.test
  lldb/test/Shell/ScriptInterpreter/Java/independent_state.test
  lldb/test/Shell/ScriptInterpreter/Java/io.test
  lldb/test/Shell/ScriptInterpreter/Java/java-python.test
  lldb/test/Shell/ScriptInterpreter/Java/java.test
  lldb/test/Shell/ScriptInterpreter/Java/lit.local.cfg
  lldb/test/Shell/ScriptInterpreter/Java/nested_sessions.test
  lldb/test/Shell/ScriptInterpreter/Java/partial_statements.test
  lldb/test/Shell/ScriptInterpreter/Java/persistent_state.test
  lldb/test/Shell/ScriptInterpreter/Java/print.test
  lldb/test/Shell/ScriptInterpreter/Java/quit.test
  lldb/test/Shell/ScriptInterpreter/Java/watchpoint_callback.test
  lldb/test/Shell/lit.cfg.py
  lldb/test/Shell/lit.site.cfg.py.in
  lldb/test/Unit/lit.cfg.py
  lldb/unittests/ScriptInterpreter/CMakeLists.txt
  lldb/unittests/ScriptInterpreter/Java/CMakeLists.txt
  lldb/unittests/ScriptInterpreter/Java/JavaTests.cpp
  lldb/unittests/ScriptInterpreter/Java/ScriptInterpreterTests.cpp

Index: lldb/unittests/ScriptInterpreter/Java/ScriptInterpreterTests.cpp
===================================================================
--- /dev/null
+++ lldb/unittests/ScriptInterpreter/Java/ScriptInterpreterTests.cpp
@@ -0,0 +1,59 @@
+//===-- JavaTests.cpp ------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/Platform/Linux/PlatformLinux.h"
+#include "Plugins/ScriptInterpreter/Java/ScriptInterpreterJava.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Utility/Reproducer.h"
+#include "gtest/gtest.h"
+
+using namespace lldb_private;
+using namespace lldb_private::repro;
+using namespace lldb;
+
+namespace {
+class ScriptInterpreterTest : public ::testing::Test {
+public:
+  void SetUp() override {
+    llvm::cantFail(Reproducer::Initialize(ReproducerMode::Off, llvm::None));
+    FileSystem::Initialize();
+    HostInfo::Initialize();
+
+    // Pretend Linux is the host platform.
+    platform_linux::PlatformLinux::Initialize();
+    ArchSpec arch("powerpc64-pc-linux");
+    Platform::SetHostPlatform(
+        platform_linux::PlatformLinux::CreateInstance(true, &arch));
+  }
+  void TearDown() override {
+    platform_linux::PlatformLinux::Terminate();
+    HostInfo::Terminate();
+    FileSystem::Terminate();
+    Reproducer::Terminate();
+  }
+};
+} // namespace
+
+TEST_F(ScriptInterpreterTest, ExecuteOneLine) {
+  DebuggerSP debugger_sp = Debugger::CreateInstance();
+  ASSERT_TRUE(debugger_sp);
+
+  ScriptInterpreterJava script_interpreter(*debugger_sp);
+  //if (llvm::Error e = script_interpreter.EnterSession(debugger_sp->GetID())) {
+  //  return;
+  //}
+  CommandReturnObject result(/*colors*/ false);
+  EXPECT_TRUE(script_interpreter.ExecuteOneLine("System.err.println(args);", &result));
+  EXPECT_FALSE(script_interpreter.ExecuteOneLine("nil = foo", &result));
+  EXPECT_TRUE(result.GetErrorData().startswith(
+      "error: java failed attempting to evaluate 'nil = foo'"));
+}
Index: lldb/unittests/ScriptInterpreter/Java/JavaTests.cpp
===================================================================
--- /dev/null
+++ lldb/unittests/ScriptInterpreter/Java/JavaTests.cpp
@@ -0,0 +1,64 @@
+//===-- JavaTests.cpp ------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/ScriptInterpreter/Java/Java.h"
+#include "gtest/gtest.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+extern "C" int javaopen_lldb(JNIEnv *L) { return 0; }
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
+
+// Disable warning C4190: 'LLDBSwigPythonBreakpointCallbackFunction' has
+// C-linkage specified, but returns UDT 'llvm::Expected<bool>' which is
+// incompatible with C
+#if _MSC_VER
+#pragma warning (push)
+#pragma warning (disable : 4190)
+#endif
+
+extern "C" llvm::Expected<bool> LLDBSwigJavaBreakpointCallbackFunction(
+    JNIEnv *L, lldb::StackFrameSP stop_frame_sp,
+    lldb::BreakpointLocationSP bp_loc_sp, StructuredDataImpl *extra_args_impl) {
+  return false;
+}
+
+extern "C" llvm::Expected<bool> LLDBSwigJavaWatchpointCallbackFunction(
+    JNIEnv *L, lldb::StackFrameSP stop_frame_sp, lldb::WatchpointSP wp_sp) {
+  return false;
+}
+
+#if _MSC_VER
+#pragma warning (pop)
+#endif
+
+#pragma clang diagnostic pop
+
+TEST(JavaTest, RunValid) {
+  Java java;
+  llvm::Error error = java.Test("int foo = 1;");
+  EXPECT_FALSE(static_cast<bool>(error));
+}
+
+TEST(JavaTest, RunError) {
+  Java java;
+  llvm::Error error = java.RunError("ERROR");
+  EXPECT_TRUE(static_cast<bool>(error));
+  EXPECT_EQ(llvm::toString(std::move(error)),"ERROR\n");
+}
+
+TEST(JavaTest, RunInvalid) {
+  Java java;
+  llvm::Error error = java.Test("foo == 1");
+  EXPECT_TRUE(static_cast<bool>(error));
+  EXPECT_EQ(llvm::toString(std::move(error)), "Failed to execute '/run'");
+}
+
Index: lldb/unittests/ScriptInterpreter/Java/CMakeLists.txt
===================================================================
--- /dev/null
+++ lldb/unittests/ScriptInterpreter/Java/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_lldb_unittest(ScriptInterpreterJavaTests
+  JavaTests.cpp
+  ScriptInterpreterTests.cpp
+
+  LINK_LIBS
+    lldbHost
+    lldbPluginScriptInterpreterJava
+    lldbPluginPlatformLinux
+    LLVMTestingSupport
+  LINK_COMPONENTS
+    Support
+  )
Index: lldb/unittests/ScriptInterpreter/CMakeLists.txt
===================================================================
--- lldb/unittests/ScriptInterpreter/CMakeLists.txt
+++ lldb/unittests/ScriptInterpreter/CMakeLists.txt
@@ -4,3 +4,6 @@
 if (LLDB_ENABLE_LUA)
   add_subdirectory(Lua)
 endif()
+if (LLDB_ENABLE_JAVA)
+  add_subdirectory(Java)
+endif()
Index: lldb/test/Unit/lit.cfg.py
===================================================================
--- lldb/test/Unit/lit.cfg.py
+++ lldb/test/Unit/lit.cfg.py
@@ -29,6 +29,9 @@
     'TEMP',
     'TMP',
     'XDG_CACHE_HOME',
+    'JAVA_HOME',
+    'CLASSPATH',
+    'LLVM_SYMBOLIZER_PATH',
 ])
 llvm_config.with_environment('PATH',
                              os.path.dirname(sys.executable),
Index: lldb/test/Shell/lit.site.cfg.py.in
===================================================================
--- lldb/test/Shell/lit.site.cfg.py.in
+++ lldb/test/Shell/lit.site.cfg.py.in
@@ -21,6 +21,7 @@
 config.lldb_bitness = 64 if @LLDB_IS_64_BITS@ else 32
 config.lldb_enable_python = @LLDB_ENABLE_PYTHON@
 config.lldb_enable_lua = @LLDB_ENABLE_LUA@
+config.lldb_enable_java = @LLDB_ENABLE_JAVA@
 config.lldb_build_directory = "@LLDB_TEST_BUILD_DIRECTORY@"
 config.lldb_system_debugserver = @LLDB_USE_SYSTEM_DEBUGSERVER@
 # The shell tests use their own module caches.
Index: lldb/test/Shell/lit.cfg.py
===================================================================
--- lldb/test/Shell/lit.cfg.py
+++ lldb/test/Shell/lit.cfg.py
@@ -45,6 +45,9 @@
     'TEMP',
     'TMP',
     'XDG_CACHE_HOME',
+    'JAVA_HOME',
+    'CLASSPATH',
+    'LLVM_SYMBOLIZER_PATH',
 ])
 
 # Support running the test suite under the lldb-repro wrapper. This makes it
@@ -118,6 +121,9 @@
 if config.lldb_enable_lua:
     config.available_features.add('lua')
 
+if config.lldb_enable_java:
+    config.available_features.add('java')
+
 if config.lldb_enable_lzma:
     config.available_features.add('lzma')
 
Index: lldb/test/Shell/ScriptInterpreter/Java/watchpoint_callback.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Java/watchpoint_callback.test
@@ -0,0 +1,32 @@
+# XFAIL: system-netbsd
+# RUN: echo "int main() { int val = 1; val++; return 0; }" | %clang_host -x c - -g -o %t
+# RUN: %lldb -s %s --script-language java %t 2>&1 | FileCheck %s
+b main
+r
+watchpoint set variable val
+watchpoint command add -s java
+System.out.println("val=" + frame.FindVariable("val").GetValue());
+quit
+c
+# CHECK: val=1
+# CHECK: val=2
+# CHECK: Process {{[0-9]+}} exited
+r
+watchpoint set variable val
+watchpoint modify 1 -c "(val == 1)"
+watchpoint command add -s java
+System.out.println("conditional watchpoint");
+SBWatchpoint wpx = target.FindWatchpointByID(wp.GetID());
+wpx.SetEnabled(false);
+quit
+c
+# CHECK-COUNT-1: conditional watchpoint
+# CHECK: Process {{[0-9]+}} exited
+r
+watchpoint set expr 0x00
+watchpoint command add -s java
+System.out.println("never triggers");
+quit
+c
+# CHECK-NOT: never triggers
+# CHECK: Process {{[0-9]+}} exited
Index: lldb/test/Shell/ScriptInterpreter/Java/quit.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Java/quit.test
@@ -0,0 +1,9 @@
+# UNSUPPORTED: lldb-repro
+#
+# RUN: cat %s | %lldb --script-language java 2>&1 | FileCheck %s
+script
+System.out.println(95000 + 126);
+quit
+target list
+# CHECK: 95126
+# CHECK: No targets
Index: lldb/test/Shell/ScriptInterpreter/Java/print.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Java/print.test
@@ -0,0 +1,29 @@
+# UNSUPPORTED: lldb-repro
+#
+# RUN: rm -rf %t.stderr %t.stdout
+# RUN: cat %s | %lldb --script-language java 2> %t.stderr > %t.stdout
+# RUN: cat %t.stdout | FileCheck %s --check-prefix STDOUT
+# RUN: cat %t.stderr | FileCheck %s --check-prefix STDERR
+script
+/main
+/init
+SBFile file = new SBFile(2, "w", false);
+debugger.SetOutputFile(file);
+System.out.println(95000 + 126);
+/close
+/run
+quit
+script
+/main
+/init
+System.err.println("SBD@"+debugger.GetID());
+/close
+/run
+quit
+
+# STDOUT: 95126
+# STDERR: SBD@1
+
+# RUN: rm -rf %t.stderr %t.stdout
+# RUN: %lldb --script-language java -o 'script System.out.println(95000 + 126);' 2> %t.stderr > %t.stdout
+# RUN: cat %t.stdout | FileCheck %s --check-prefix STDOUT
Index: lldb/test/Shell/ScriptInterpreter/Java/persistent_state.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Java/persistent_state.test
@@ -0,0 +1,2 @@
+# RUN: %lldb --script-language java -o 'script /shell int foo = 1010;' -o 'script /shell int bar = 101;' -o 'script /shell foo+bar;' 2>&1 | FileCheck %s
+# CHECK: 1111
Index: lldb/test/Shell/ScriptInterpreter/Java/partial_statements.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Java/partial_statements.test
@@ -0,0 +1,19 @@
+# RUN: %lldb -s %s --script-language java 2>&1 | FileCheck %s
+script
+/main
+{
+int a = 123;
+System.out.println(a);
+}
+/close
+/run
+/main
+System.out.println(str);
+}
+static String str = "hello there!";
+}
+/run
+quit
+# CHECK: 123
+# CHECK: hello there!
+# CHECK-NOT: error
Index: lldb/test/Shell/ScriptInterpreter/Java/nested_sessions.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Java/nested_sessions.test
@@ -0,0 +1,10 @@
+# RUN: mkdir -p %t
+# RUN: echo "int main() { return 0; }" | %clang_host -x c - -o %t/foo
+# RUN: echo "int main() { return 0; }" | %clang_host -x c - -o %t/bar
+# RUN:  %lldb --script-language java -o "file %t/bar" -o "file %t/foo" -s %S/Inputs/nested_sessions.in  -s %S/Inputs/nested_sessions_2.in 2>&1 | FileCheck %s
+# CHECK: script
+# CHECK: foo foo
+# CHECK-NEXT: foo bar
+# CHECK-NEXT: foo bar
+# CHECK: script
+# CHECK-NEXT: bar bar
Index: lldb/test/Shell/ScriptInterpreter/Java/lit.local.cfg
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Java/lit.local.cfg
@@ -0,0 +1,3 @@
+if 'java' not in config.available_features:
+  config.unsupported = True
+
Index: lldb/test/Shell/ScriptInterpreter/Java/java.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Java/java.test
@@ -0,0 +1,6 @@
+# RUN: %lldb --script-language java -o 'script System.out.println(1000+100+10+1);' 2>&1 | FileCheck %s
+# RUN: %lldb --script-language java -o 'script -- System.out.println(1000+100+10+1);' 2>&1 | FileCheck %s
+# RUN: %lldb --script-language java -o 'script --language default -- System.out.println(1000+100+10+1);' 2>&1 | FileCheck %s
+# RUN: %lldb -o 'script -l java -- System.out.println(1000+100+10+1);' 2>&1 | FileCheck %s
+# RUN: %lldb -o 'script --language java -- System.out.println(1000+100+10+1);' 2>&1 | FileCheck %s
+# CHECK: 1111
Index: lldb/test/Shell/ScriptInterpreter/Java/java-python.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Java/java-python.test
@@ -0,0 +1,20 @@
+# REQUIRES: python
+# UNSUPPORTED: lldb-repro
+
+# RUN: mkdir -p %t
+# RUN: cd %t
+# RUN: echo "int main() { return 0; }" | %clang_host -x c - -o a.out
+# RUN: cat %s | %lldb 2>&1 | FileCheck %s
+script -l java --
+/main
+/init
+target = debugger.CreateTarget("a.out");
+System.out.println("target is valid: " + target.IsValid());
+debugger.SetSelectedTarget(target);
+/close
+/run
+quit
+# CHECK: target is valid: true
+script -l python --
+print("selected target: {}".format(lldb.debugger.GetSelectedTarget()))
+# CHECK: selected target: a.out
Index: lldb/test/Shell/ScriptInterpreter/Java/io.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Java/io.test
@@ -0,0 +1,27 @@
+# UNSUPPORTED: lldb-repro
+#
+# RUN: rm -rf %t.stderr %t.stdout
+# RUN: cat %s | %lldb --script-language java 2> %t.stderr > %t.stdout
+# RUN: cat %t.stdout | FileCheck %s --check-prefix STDOUT
+# RUN: cat %t.stderr | FileCheck %s --check-prefix STDERR
+script
+/main
+/init
+SBFile file = new SBFile(2, "w", false);
+debugger.SetOutputFile(file);
+System.out.println(95000 + 126);
+/close
+/run
+quit
+script
+/main
+System.err.println(95000 + 14);
+/close
+/run
+
+# STDOUT: 95126
+# STDERR: 95014
+
+# RUN: rm -rf %t.stderr %t.stdout
+# RUN: %lldb --script-language java -o 'script System.err.println(95000 + 126);' 2> %t.stderr > %t.stdout
+# RUN: cat %t.stderr | FileCheck %s --check-prefix STDOUT
Index: lldb/test/Shell/ScriptInterpreter/Java/independent_state.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Java/independent_state.test
@@ -0,0 +1,5 @@
+# RUN:  %lldb --script-language java -s %S/Inputs/independent_state.in 2>&1 | FileCheck %s
+# CHECK: 47
+# CHECK: 47
+# CHECK: 42
+# CHECK: 42
Index: lldb/test/Shell/ScriptInterpreter/Java/fail_breakpoint_oneline.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Java/fail_breakpoint_oneline.test
@@ -0,0 +1,4 @@
+# RUN: %lldb -s %s --script-language java 2>&1 | FileCheck %s
+b main
+breakpoint command add -s java -o '1234_foo'
+# CHECK: error: Failed to execute '/compile'
Index: lldb/test/Shell/ScriptInterpreter/Java/convenience_variables.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Java/convenience_variables.test
@@ -0,0 +1,21 @@
+# UNSUPPORTED: lldb-repro
+#
+# This tests that the convenience variables are not nil. Given that there is no
+# target we only expect the debugger to be valid.
+#
+# RUN: cat %s | %lldb --script-language java 2>&1 | FileCheck %s
+script
+/main
+/init
+System.out.println("debugger is valid: " + debugger.IsValid());
+System.out.println("target is valid: " + target.IsValid());
+System.out.println("process is valid: " + process.IsValid());
+System.out.println("thread is valid: " + thread.IsValid());
+System.out.println("frame is valid: " + frame.IsValid());
+/close
+/run
+# CHECK: debugger is valid: true
+# CHECK: target is valid: false
+# CHECK: process is valid: false
+# CHECK: thread is valid: false
+# CHECK: frame is valid: false
Index: lldb/test/Shell/ScriptInterpreter/Java/command_script_import.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Java/command_script_import.test
@@ -0,0 +1,12 @@
+# RUN: %lldb --script-language java -o 'command script import %S/Inputs/testmodule.java' -o 'script testmodule.foo();' 2>&1 | FileCheck %s
+# CHECK: Hello World!
+
+# RUN: mkdir -p %t
+# RUN: cp %S/Inputs/testmodule.java %t/testmodule.notjava
+# RUN: %lldb --script-language java -o 'command script import %t/testmodule.notjava' -o 'script testmodule.foo();' 2>&1 | FileCheck %s --check-prefix EXTENSION
+# EXTENSION: error: module importing failed: java failed to import '{{.*}}testmodule.notjava': invalid extension
+# EXTENSION-NOT: Hello World!
+
+# RUN: %lldb --script-language java -o 'command script import %S/Inputs/bogus' -o 'script testmodule.foo();' 2>&1 | FileCheck %s --check-prefix NONEXISTING
+# NONEXISTING: error: module importing failed: java failed to import '{{.*}}bogus': invalid path
+# NONEXISTING-NOT: Hello World!
Index: lldb/test/Shell/ScriptInterpreter/Java/breakpoint_oneline_callback.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Java/breakpoint_oneline_callback.test
@@ -0,0 +1,18 @@
+# RUN: echo "int main() { return 0; }" | %clang_host -x c - -o %t
+# RUN: %lldb -s %s --script-language java %t 2>&1 | FileCheck %s
+b main
+breakpoint command add -s java -o 'return false;'
+run
+# CHECK: Process {{[0-9]+}} exited with status = 0
+breakpoint command add -s java -o 'System.out.println("bacon");'
+run
+# CHECK: bacon
+# CHECK: Process {{[0-9]+}} exited with status = 0
+breakpoint command add -s java -o "return true;"
+run
+# CHECK: Process {{[0-9]+}} stopped
+breakpoint command add -s java -o 'System.out.println("my error message");'
+run
+# CHECK: my error message
+# CHECK: Process {{[0-9]+}} exited with status = 0
+
Index: lldb/test/Shell/ScriptInterpreter/Java/breakpoint_function_callback.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Java/breakpoint_function_callback.test
@@ -0,0 +1,23 @@
+# RUN: echo "int main() { return 0; }" | %clang_host -x c - -o %t
+# RUN: %lldb -s %s --script-language java %t 2>&1 | FileCheck %s
+b main
+script
+/bpt abc
+System.out.println(args);
+if (args != null) {
+   SBStream stream = new SBStream();
+   SBStructuredData val = args.GetValueForKey("foo");
+   val.GetDescription(stream);
+   System.out.println(stream.GetData());
+}
+return true;
+/close
+/compile
+quit
+breakpoint command add -s java -F abc
+r
+# CHECK: null
+breakpoint command add -s java -F abc -k foo -v 123pizza!
+r
+# CHECK: "123pizza!"
+
Index: lldb/test/Shell/ScriptInterpreter/Java/breakpoint_callback.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Java/breakpoint_callback.test
@@ -0,0 +1,11 @@
+# RUN: echo "int main() { return 0; }" | %clang_host -x c - -o %t
+# RUN: %lldb -s %s --script-language java %t 2>&1 | FileCheck %s
+b main
+breakpoint command add -s java
+int a = 123;
+System.out.println(a);
+return true;
+quit
+run
+# CHECK: 123
+
Index: lldb/test/Shell/ScriptInterpreter/Java/bindings.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Java/bindings.test
@@ -0,0 +1,8 @@
+# RUN: cat %s | %lldb --script-language java 2>&1 | FileCheck %s
+script
+/main
+SBDebugger debugger = SBDebugger.Create();
+System.err.println("debugger is valid: " + debugger.IsValid());
+/close
+/run
+# CHECK: debugger is valid: true
Index: lldb/test/Shell/ScriptInterpreter/Java/Inputs/testmodule.java
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Java/Inputs/testmodule.java
@@ -0,0 +1,3 @@
+class testmodule {
+  public static void foo() { System.out.println("Hello World!"); }
+}
Index: lldb/test/Shell/ScriptInterpreter/Java/Inputs/nested_sessions_2.in
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Java/Inputs/nested_sessions_2.in
@@ -0,0 +1,8 @@
+script
+/main
+/init
+    System.out.println(n(target) + " " + n(debugger.GetSelectedTarget()));
+  }
+  private static String n(SBTarget t) {return t.GetExecutable().GetFilename();}
+}
+/run
Index: lldb/test/Shell/ScriptInterpreter/Java/Inputs/nested_sessions.in
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Java/Inputs/nested_sessions.in
@@ -0,0 +1,12 @@
+script
+/main
+/init
+    System.out.println(n(target) + " " + n(debugger.GetSelectedTarget()));
+    debugger.SetSelectedTarget(debugger.GetTargetAtIndex(0));
+    System.out.println(n(target) + " " +  n(debugger.GetSelectedTarget()));
+    //debugger.HandleCommand("script SBDebugger debugger = SBDebugger.FindDebuggerWithID(1); SBTarget target =     debugger.GetSelectedTarget(); System.out.println(target.GetExecutable().GetFilename() + \" \" + debugger.GetSelectedTarget().GetExecutable().GetFilename());");
+    System.out.println(n(target)  + " " +  n(debugger.GetSelectedTarget()));
+  }
+  private static String n(SBTarget t) {return t.GetExecutable().GetFilename();}
+}
+/run
Index: lldb/test/Shell/ScriptInterpreter/Java/Inputs/independent_state.in
===================================================================
--- /dev/null
+++ lldb/test/Shell/ScriptInterpreter/Java/Inputs/independent_state.in
@@ -0,0 +1,10 @@
+script
+/main
+int foobar = 40 + 7;
+System.out.println(foobar);
+SBDebugger d = SBDebugger.Create();
+d.HandleCommand("script foobar = 40 + 2;");
+System.out.println(foobar);
+d.HandleCommand("script print(foobar);");
+/close
+/run
Index: lldb/test/CMakeLists.txt
===================================================================
--- lldb/test/CMakeLists.txt
+++ lldb/test/CMakeLists.txt
@@ -172,6 +172,7 @@
   LLDB_BUILD_INTEL_PT
   LLDB_ENABLE_PYTHON
   LLDB_ENABLE_LUA
+  LLDB_ENABLE_JAVA
   LLDB_ENABLE_LZMA
   LLVM_ENABLE_ZLIB
   LLVM_ENABLE_SHARED_LIBS
Index: lldb/source/Plugins/ScriptInterpreter/Java/ScriptInterpreterJava.h
===================================================================
--- /dev/null
+++ lldb/source/Plugins/ScriptInterpreter/Java/ScriptInterpreterJava.h
@@ -0,0 +1,120 @@
+//===-- ScriptInterpreterJava.h ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ScriptInterpreterJava_h_
+#define liblldb_ScriptInterpreterJava_h_
+
+#include <vector>
+
+#include "lldb/Breakpoint/WatchpointOptions.h"
+#include "lldb/Core/StructuredDataImpl.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-enumerations.h"
+
+namespace lldb_private {
+class Java;
+class ScriptInterpreterJava : public ScriptInterpreter {
+public:
+  class CommandDataJava : public BreakpointOptions::CommandData {
+  public:
+    CommandDataJava() : BreakpointOptions::CommandData() {
+      interpreter = lldb::eScriptLanguageJava;
+    }
+    CommandDataJava(StructuredData::ObjectSP extra_args_sp)
+        : BreakpointOptions::CommandData(), m_extra_args_sp(extra_args_sp) {
+      interpreter = lldb::eScriptLanguageJava;
+    }
+    StructuredData::ObjectSP m_extra_args_sp;
+  };
+
+  ScriptInterpreterJava(Debugger &debugger);
+
+  ~ScriptInterpreterJava() override;
+
+  bool ExecuteOneLine(
+      llvm::StringRef command, CommandReturnObject *result,
+      const ExecuteScriptOptions &options = ExecuteScriptOptions()) override;
+
+  void ExecuteInterpreterLoop() override;
+
+  bool LoadScriptingModule(const char *filename,
+                           const LoadScriptOptions &options,
+                           lldb_private::Status &error,
+                           StructuredData::ObjectSP *module_sp = nullptr,
+                           FileSpec extra_search_dir = {}) override;
+
+  // Static Functions
+  static void Initialize();
+
+  static void Terminate();
+
+  static lldb::ScriptInterpreterSP CreateInstance(Debugger &debugger);
+
+  static lldb_private::ConstString GetPluginNameStatic();
+
+  static const char *GetPluginDescriptionStatic();
+
+  static bool BreakpointCallbackFunction(void *baton,
+                                         StoppointCallbackContext *context,
+                                         lldb::user_id_t break_id,
+                                         lldb::user_id_t break_loc_id);
+
+  static bool WatchpointCallbackFunction(void *baton,
+                                         StoppointCallbackContext *context,
+                                         lldb::user_id_t watch_id);
+
+  // PluginInterface protocol
+  lldb_private::ConstString GetPluginName() override;
+
+  Java &GetJava();
+
+  llvm::Error EnterSession(lldb::user_id_t debugger_id);
+  llvm::Error LeaveSession();
+
+  void CollectDataForBreakpointCommandCallback(
+      std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
+      CommandReturnObject &result) override;
+
+  void
+  CollectDataForWatchpointCommandCallback(WatchpointOptions *wp_options,
+                                          CommandReturnObject &result) override;
+
+  Status SetBreakpointCommandCallback(BreakpointOptions &bp_options,
+                                      const char *command_body_text) override;
+
+  void SetWatchpointCommandCallback(WatchpointOptions *wp_options,
+                                    const char *command_body_text) override;
+
+  Status SetBreakpointCommandCallbackFunction(
+      BreakpointOptions &bp_options, const char *function_name,
+      StructuredData::ObjectSP extra_args_sp) override;
+
+  Status
+  SetWatchpointCommandCallbackFunction(WatchpointOptions *wp_options,
+                                       const char *function_name,
+                                       StructuredData::ObjectSP extra_args_sp);
+
+private:
+  std::unique_ptr<Java> m_java;
+  bool m_session_is_active = false;
+
+  Status RegisterBreakpointCallback(BreakpointOptions &bp_options,
+                                    const char *command_body_text,
+                                    StructuredData::ObjectSP extra_args_sp,
+                                    bool existing);
+
+  Status RegisterWatchpointCallback(WatchpointOptions *wp_options,
+                                    const char *command_body_text,
+                                    StructuredData::ObjectSP extra_args_sp,
+                                    bool existing);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ScriptInterpreterJava_h_
Index: lldb/source/Plugins/ScriptInterpreter/Java/ScriptInterpreterJava.cpp
===================================================================
--- /dev/null
+++ lldb/source/Plugins/ScriptInterpreter/Java/ScriptInterpreterJava.cpp
@@ -0,0 +1,416 @@
+//===-- ScriptInterpreterJava.cpp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ScriptInterpreterJava.h"
+#include "Java.h"
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StringList.h"
+#include "lldb/Utility/Timer.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/FormatAdapters.h"
+#include <memory>
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+LLDB_PLUGIN_DEFINE(ScriptInterpreterJava)
+
+enum ActiveIOHandler {
+  eIOHandlerNone,
+  eIOHandlerBreakpoint,
+  eIOHandlerWatchpoint
+};
+
+class IOHandlerJavaInterpreter : public IOHandlerDelegate,
+                                 public IOHandlerEditline {
+public:
+  IOHandlerJavaInterpreter(Debugger &debugger,
+                           ScriptInterpreterJava &script_interpreter,
+                           ActiveIOHandler active_io_handler = eIOHandlerNone)
+      : IOHandlerEditline(debugger, IOHandler::Type::JavaInterpreter, "java",
+                          ">>> ", "..> ", true, debugger.GetUseColor(), 0,
+                          *this, nullptr),
+        m_script_interpreter(script_interpreter),
+        m_active_io_handler(active_io_handler) {
+    llvm::cantFail(m_script_interpreter.EnterSession(debugger.GetID()));
+    llvm::cantFail(m_script_interpreter.GetJava().ChangeIO(
+        debugger.GetOutputFile().GetStream(),
+        debugger.GetErrorFile().GetStream()));
+  }
+
+  ~IOHandlerJavaInterpreter() override {
+    llvm::cantFail(m_script_interpreter.LeaveSession());
+  }
+
+  void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
+    const char *instructions = nullptr;
+    switch (m_active_io_handler) {
+    case eIOHandlerNone:
+      instructions =
+          "Enter your Java command(s). Type 'quit' to end.\n"
+          "Special commands include: \n"
+          "   /main      - sets up the standard prologue\n"
+          "   /bpt, /wpt - prologue for break & watchpoint handlers (not "
+          "required for 'br' & 'wa')\n"
+          "   /init      - defines debugger, target, process, thread, frame\n"
+          "   /close     - completes prologue\n"
+          "   /reset     - start over\n"
+          "   /compile   - compile a function (no args == current)\n"
+          "   /exec      - execute a function\n"
+          "   /run       - compile & execute\n"
+          "Sample sequence:   /main, /init, "
+          "System.out.println(frame.GetPC());, /close, /run\n";
+      break;
+    case eIOHandlerWatchpoint:
+      instructions = "Enter your Java command(s). Type 'quit' to end.\n"
+                     "The commands are compiled as the body of the following "
+                     "Java function:\n"
+                     "  boolean callback(_frame, wp) {}\n";
+      SetPrompt(llvm::StringRef("..> "));
+      break;
+    case eIOHandlerBreakpoint:
+      instructions = "Enter your Java command(s). Type 'quit' to end.\n"
+                     "The commands are compiled as the body of the following "
+                     "Java function:\n"
+                     "  boolean callback(_frame, loc, args) {}\n";
+      SetPrompt(llvm::StringRef("..> "));
+      break;
+    }
+    if (instructions == nullptr)
+      return;
+    if (interactive)
+      *io_handler.GetOutputStreamFileSP() << instructions;
+  }
+
+  bool IOHandlerIsInputComplete(IOHandler &io_handler,
+                                StringList &lines) override {
+    size_t last = lines.GetSize() - 1;
+    if (IsQuitCommand(lines.GetStringAtIndex(last))) {
+      if (m_active_io_handler == eIOHandlerBreakpoint ||
+          m_active_io_handler == eIOHandlerWatchpoint)
+        lines.DeleteStringAtIndex(last);
+      return true;
+    }
+    StreamString str;
+    lines.Join("\n", str);
+    if (llvm::Error E =
+            m_script_interpreter.GetJava().CheckSyntax(str.GetString())) {
+      std::string error_str = toString(std::move(E));
+      // Java always errors out to incomplete code with '<eof>'
+      return error_str.find("<eof>") == std::string::npos;
+    }
+    // The breakpoint and watchpoint handler only exits with a explicit 'quit'
+    return m_active_io_handler != eIOHandlerBreakpoint &&
+           m_active_io_handler != eIOHandlerWatchpoint;
+  }
+
+  void IOHandlerInputComplete(IOHandler &io_handler,
+                              std::string &data) override {
+    switch (m_active_io_handler) {
+    case eIOHandlerBreakpoint: {
+      auto *bp_options_vec =
+          static_cast<std::vector<std::reference_wrapper<BreakpointOptions>> *>(
+              io_handler.GetUserData());
+      for (BreakpointOptions &bp_options : *bp_options_vec) {
+        Status error = m_script_interpreter.SetBreakpointCommandCallback(
+            bp_options, data.c_str());
+        if (error.Fail())
+          *io_handler.GetErrorStreamFileSP() << error.AsCString() << '\n';
+      }
+      io_handler.SetIsDone(true);
+    } break;
+    case eIOHandlerWatchpoint: {
+      auto *wp_options =
+          static_cast<WatchpointOptions *>(io_handler.GetUserData());
+      m_script_interpreter.SetWatchpointCommandCallback(wp_options,
+                                                        data.c_str());
+      io_handler.SetIsDone(true);
+    } break;
+    case eIOHandlerNone:
+      if (IsQuitCommand(data)) {
+        io_handler.SetIsDone(true);
+        return;
+      }
+      if (llvm::Error error = m_script_interpreter.GetJava().Run(data))
+        *io_handler.GetErrorStreamFileSP() << toString(std::move(error));
+      break;
+    }
+  }
+
+private:
+  ScriptInterpreterJava &m_script_interpreter;
+  ActiveIOHandler m_active_io_handler;
+
+  bool IsQuitCommand(llvm::StringRef cmd) { return cmd.rtrim() == "quit"; }
+};
+
+ScriptInterpreterJava::ScriptInterpreterJava(Debugger &debugger)
+    : ScriptInterpreter(debugger, eScriptLanguageJava),
+      m_java(std::make_unique<Java>()) {}
+
+ScriptInterpreterJava::~ScriptInterpreterJava() = default;
+
+bool ScriptInterpreterJava::ExecuteOneLine(
+    llvm::StringRef command, CommandReturnObject *result,
+    const ExecuteScriptOptions &options) {
+  if (command.empty()) {
+    if (result)
+      result->AppendError("empty command passed to java\n");
+    return false;
+  }
+
+  llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>>
+      io_redirect_or_error = ScriptInterpreterIORedirect::Create(
+          options.GetEnableIO(), m_debugger, result);
+  if (!io_redirect_or_error) {
+    if (result)
+      result->AppendErrorWithFormatv(
+          "failed to redirect I/O: {0}\n",
+          llvm::fmt_consume(io_redirect_or_error.takeError()));
+    else
+      llvm::consumeError(io_redirect_or_error.takeError());
+    return false;
+  }
+
+  ScriptInterpreterIORedirect &io_redirect = **io_redirect_or_error;
+
+  if (llvm::Error e =
+          m_java->ChangeIO(io_redirect.GetOutputFile()->GetStream(),
+                           io_redirect.GetErrorFile()->GetStream())) {
+    result->AppendErrorWithFormatv("java failed to redirect I/O: {0}\n",
+                                   llvm::toString(std::move(e)));
+    return false;
+  }
+
+  if (llvm::Error e = m_java->Test(command)) {
+    result->AppendErrorWithFormatv(
+        "java failed attempting to evaluate '{0}': {1}\n", command,
+        llvm::toString(std::move(e)));
+    return false;
+  }
+
+  io_redirect.Flush();
+  return true;
+}
+
+void ScriptInterpreterJava::ExecuteInterpreterLoop() {
+  LLDB_SCOPED_TIMER();
+
+  // At the moment, the only time the debugger does not have an input file
+  // handle is when this is called directly from java, in which case it is
+  // both dangerous and unnecessary (not to mention confusing) to try to embed
+  // a running interpreter loop inside the already running java interpreter
+  // loop, so we won't do it.
+  if (!m_debugger.GetInputFile().IsValid())
+    return;
+
+  IOHandlerSP io_handler_sp(new IOHandlerJavaInterpreter(m_debugger, *this));
+  m_debugger.RunIOHandlerAsync(io_handler_sp);
+}
+
+bool ScriptInterpreterJava::LoadScriptingModule(
+    const char *filename, const LoadScriptOptions &options,
+    lldb_private::Status &error, StructuredData::ObjectSP *module_sp,
+    FileSpec extra_search_dir) {
+
+  FileSystem::Instance().Collect(filename);
+  if (llvm::Error e = m_java->LoadModule(filename)) {
+    error.SetErrorStringWithFormatv("java failed to import '{0}': {1}\n",
+                                    filename, llvm::toString(std::move(e)));
+    return false;
+  }
+  return true;
+}
+
+void ScriptInterpreterJava::Initialize() {
+  static llvm::once_flag g_once_flag;
+
+  llvm::call_once(g_once_flag, []() {
+    PluginManager::RegisterPlugin(GetPluginNameStatic(),
+                                  GetPluginDescriptionStatic(),
+                                  lldb::eScriptLanguageJava, CreateInstance);
+  });
+}
+
+void ScriptInterpreterJava::Terminate() {}
+
+llvm::Error ScriptInterpreterJava::EnterSession(user_id_t debugger_id) {
+  // printf("EnterSession %x\n", m_session_is_active);
+  if (m_session_is_active)
+    return llvm::Error::success();
+
+  m_session_is_active = true;
+  return m_java->Init(debugger_id);
+}
+
+llvm::Error ScriptInterpreterJava::LeaveSession() {
+  if (!m_session_is_active)
+    return llvm::Error::success();
+
+  m_session_is_active = false;
+  return llvm::Error::success();
+}
+
+bool ScriptInterpreterJava::BreakpointCallbackFunction(
+    void *baton, StoppointCallbackContext *context, user_id_t break_id,
+    user_id_t break_loc_id) {
+  assert(context);
+
+  ExecutionContext exe_ctx(context->exe_ctx_ref);
+  Target *target = exe_ctx.GetTargetPtr();
+  if (target == nullptr)
+    return true;
+
+  StackFrameSP stop_frame_sp(exe_ctx.GetFrameSP());
+  BreakpointSP breakpoint_sp = target->GetBreakpointByID(break_id);
+  BreakpointLocationSP bp_loc_sp(breakpoint_sp->FindLocationByID(break_loc_id));
+
+  Debugger &debugger = target->GetDebugger();
+  ScriptInterpreterJava *java_interpreter =
+      static_cast<ScriptInterpreterJava *>(
+          debugger.GetScriptInterpreter(true, eScriptLanguageJava));
+  Java &java = java_interpreter->GetJava();
+
+  CommandDataJava *bp_option_data = static_cast<CommandDataJava *>(baton);
+  llvm::Expected<bool> BoolOrErr = java.CallBreakpointCallback(
+      baton, stop_frame_sp, bp_loc_sp, bp_option_data->m_extra_args_sp);
+  if (llvm::Error E = BoolOrErr.takeError()) {
+    debugger.GetErrorStream() << toString(std::move(E));
+    return true;
+  }
+
+  return *BoolOrErr;
+}
+
+bool ScriptInterpreterJava::WatchpointCallbackFunction(
+    void *baton, StoppointCallbackContext *context, user_id_t watch_id) {
+  assert(context);
+
+  ExecutionContext exe_ctx(context->exe_ctx_ref);
+  Target *target = exe_ctx.GetTargetPtr();
+  if (target == nullptr)
+    return true;
+
+  StackFrameSP stop_frame_sp(exe_ctx.GetFrameSP());
+  WatchpointSP wp_sp = target->GetWatchpointList().FindByID(watch_id);
+
+  Debugger &debugger = target->GetDebugger();
+  ScriptInterpreterJava *java_interpreter =
+      static_cast<ScriptInterpreterJava *>(
+          debugger.GetScriptInterpreter(true, eScriptLanguageJava));
+  Java &java = java_interpreter->GetJava();
+
+  llvm::Expected<bool> BoolOrErr =
+      java.CallWatchpointCallback(baton, stop_frame_sp, wp_sp);
+  if (llvm::Error E = BoolOrErr.takeError()) {
+    debugger.GetErrorStream() << toString(std::move(E));
+    return true;
+  }
+
+  return *BoolOrErr;
+}
+
+void ScriptInterpreterJava::CollectDataForBreakpointCommandCallback(
+    std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
+    CommandReturnObject &result) {
+  IOHandlerSP io_handler_sp(
+      new IOHandlerJavaInterpreter(m_debugger, *this, eIOHandlerBreakpoint));
+  io_handler_sp->SetUserData(&bp_options_vec);
+  m_debugger.RunIOHandlerAsync(io_handler_sp);
+}
+
+void ScriptInterpreterJava::CollectDataForWatchpointCommandCallback(
+    WatchpointOptions *wp_options, CommandReturnObject &result) {
+  IOHandlerSP io_handler_sp(
+      new IOHandlerJavaInterpreter(m_debugger, *this, eIOHandlerWatchpoint));
+  io_handler_sp->SetUserData(wp_options);
+  m_debugger.RunIOHandlerAsync(io_handler_sp);
+}
+
+Status ScriptInterpreterJava::SetBreakpointCommandCallbackFunction(
+    BreakpointOptions &bp_options, const char *function_name,
+    StructuredData::ObjectSP extra_args_sp) {
+  return RegisterBreakpointCallback(bp_options, function_name, extra_args_sp,
+                                    true);
+}
+
+Status ScriptInterpreterJava::SetBreakpointCommandCallback(
+    BreakpointOptions &bp_options, const char *command_body_text) {
+  return RegisterBreakpointCallback(bp_options, command_body_text, {}, false);
+}
+
+Status ScriptInterpreterJava::RegisterBreakpointCallback(
+    BreakpointOptions &bp_options, const char *command_body_text,
+    StructuredData::ObjectSP extra_args_sp, bool existing) {
+  Status error;
+  auto data_up = std::make_unique<CommandDataJava>(extra_args_sp);
+  error = m_java->RegisterBreakpointCallback(data_up.get(), command_body_text,
+                                             existing);
+  if (error.Fail())
+    return error;
+  auto baton_sp =
+      std::make_shared<BreakpointOptions::CommandBaton>(std::move(data_up));
+  bp_options.SetCallback(ScriptInterpreterJava::BreakpointCallbackFunction,
+                         baton_sp);
+  return error;
+}
+
+Status ScriptInterpreterJava::SetWatchpointCommandCallbackFunction(
+    WatchpointOptions *wp_options, const char *function_name,
+    StructuredData::ObjectSP extra_args_sp) {
+  return RegisterWatchpointCallback(wp_options, function_name, extra_args_sp,
+                                    true);
+}
+
+void ScriptInterpreterJava::SetWatchpointCommandCallback(
+    WatchpointOptions *wp_options, const char *command_body_text) {
+  RegisterWatchpointCallback(wp_options, command_body_text, {}, false);
+}
+
+Status ScriptInterpreterJava::RegisterWatchpointCallback(
+    WatchpointOptions *wp_options, const char *command_body_text,
+    StructuredData::ObjectSP extra_args_sp, bool existing) {
+  Status error;
+  auto data_up = std::make_unique<WatchpointOptions::CommandData>();
+  error = m_java->RegisterWatchpointCallback(data_up.get(), command_body_text,
+                                             existing);
+  if (error.Fail())
+    return error;
+  auto baton_sp =
+      std::make_shared<WatchpointOptions::CommandBaton>(std::move(data_up));
+  wp_options->SetCallback(ScriptInterpreterJava::WatchpointCallbackFunction,
+                          baton_sp);
+  return error;
+}
+
+lldb::ScriptInterpreterSP
+ScriptInterpreterJava::CreateInstance(Debugger &debugger) {
+  return std::make_shared<ScriptInterpreterJava>(debugger);
+}
+
+lldb_private::ConstString ScriptInterpreterJava::GetPluginNameStatic() {
+  static ConstString g_name("script-java");
+  return g_name;
+}
+
+const char *ScriptInterpreterJava::GetPluginDescriptionStatic() {
+  return "Java script interpreter";
+}
+
+lldb_private::ConstString ScriptInterpreterJava::GetPluginName() {
+  return GetPluginNameStatic();
+}
+
+Java &ScriptInterpreterJava::GetJava() { return *m_java; }
Index: lldb/source/Plugins/ScriptInterpreter/Java/Java.h
===================================================================
--- /dev/null
+++ lldb/source/Plugins/ScriptInterpreter/Java/Java.h
@@ -0,0 +1,66 @@
+//===-- ScriptInterpreterJava.h ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_Java_h_
+#define liblldb_Java_h_
+
+#include "jni.h"
+#include "lldb/API/SBBreakpointLocation.h"
+#include "lldb/API/SBFrame.h"
+#include "lldb/Core/StructuredDataImpl.h"
+#include "lldb/lldb-types.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+
+//#include "Java.hpp"
+
+#include <mutex>
+
+namespace lldb_private {
+
+extern "C" {
+int Javaopen_lldb(JNIEnv *env);
+}
+
+class Java {
+public:
+  Java();
+  ~Java();
+
+  llvm::Error Init(lldb::user_id_t debugger_id);
+  llvm::Error Test(llvm::StringRef buffer);
+  llvm::Error Run(llvm::StringRef buffer);
+  llvm::Error RunError(llvm::StringRef buffer);
+  llvm::Error Run(const char *data);
+  llvm::Error RegisterBreakpointCallback(void *baton, const char *body,
+                                         bool existing);
+  llvm::Expected<bool>
+  CallBreakpointCallback(void *baton, lldb::StackFrameSP stop_frame_sp,
+                         lldb::BreakpointLocationSP bp_loc_sp,
+                         StructuredData::ObjectSP extra_args_sp);
+  llvm::Error RegisterWatchpointCallback(void *baton, const char *body,
+                                         bool existing);
+  llvm::Expected<bool> CallWatchpointCallback(void *baton,
+                                              lldb::StackFrameSP stop_frame_sp,
+                                              lldb::WatchpointSP wp_sp);
+  llvm::Error LoadModule(llvm::StringRef filename);
+  llvm::Error CheckSyntax(llvm::StringRef buffer);
+  llvm::Error ChangeIO(FILE *out, FILE *err);
+
+private:
+  JavaVM *m_vm = NULL;
+  JNIEnv *m_env = NULL;
+  jclass interpreter_cls = NULL;
+  jobject interpreter = NULL;
+  const char *swigJarPath = NULL;
+  lldb::user_id_t dbg_id = -1;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_Java_h_
Index: lldb/source/Plugins/ScriptInterpreter/Java/Java.cpp
===================================================================
--- /dev/null
+++ lldb/source/Plugins/ScriptInterpreter/Java/Java.cpp
@@ -0,0 +1,395 @@
+//===-- Java.cpp -----------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Java.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Utility/FileSpec.h"
+#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FormatVariadic.h"
+#include <stdio.h>
+
+using namespace lldb_private;
+using namespace lldb;
+using namespace llvm;
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
+
+// Disable warning C4190: 'LLDBSwigPythonBreakpointCallbackFunction' has
+// C-linkage specified, but returns UDT 'llvm::Expected<bool>' which is
+// incompatible with C
+#if _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4190)
+#endif
+
+extern "C" llvm::Expected<bool> LLDBSwigJavaBreakpointCallbackFunction(
+    JNIEnv *L, void *baton, lldb::user_id_t debugger_id,
+    const char *swigJarPath, lldb::StackFrameSP stop_frame_sp,
+    lldb::BreakpointLocationSP bp_loc_sp, StructuredDataImpl *extra_args_impl);
+
+extern "C" llvm::Expected<bool> LLDBSwigJavaWatchpointCallbackFunction(
+    JNIEnv *L, void *baton, lldb::user_id_t debugger_id,
+    const char *swigJarPath, lldb::StackFrameSP stop_frame_sp,
+    lldb::WatchpointSP wp_sp);
+
+#if _MSC_VER
+#pragma warning(pop)
+#endif
+
+#pragma clang diagnostic pop
+
+bool loadLibJVM();
+bool loadSwigJar();
+
+Java::Java() {
+
+  if (!loadLibJVM()) {
+    return;
+  }
+
+  swigJarPath = ::getenv("CLASSPATH");
+  if (swigJarPath == NULL) {
+    printf("Failed to find SWIG.jar in CLASSPATH=%s\n", swigJarPath);
+    return;
+  }
+
+  JavaVMInitArgs vm_args;
+  JavaVMOption *options = new JavaVMOption[1];
+  std::string javaClassPath =
+      llvm::formatv("-Djava.class.path={0}", swigJarPath).str();
+  options[0].optionString = strdup(javaClassPath.c_str());
+  options[0].extraInfo = NULL;
+
+  vm_args.version = JNI_VERSION_1_2;
+  vm_args.nOptions = 1;
+  vm_args.options = options;
+  vm_args.ignoreUnrecognized = false;
+
+  jint (*JNI_CreateJavaVM)(JavaVM * *p_vm, void **p_env, void *vm_args);
+  jint (*JNI_GetCreatedJavaVMs)(JavaVM * *vmBuf, jsize bufLen, jsize * nVMs);
+
+  void *fn_ptr =
+      sys::DynamicLibrary::SearchForAddressOfSymbol("JNI_GetCreatedJavaVMs");
+  JNI_GetCreatedJavaVMs = (jint(*)(JavaVM **, jsize, jsize *))fn_ptr;
+  fn_ptr = sys::DynamicLibrary::SearchForAddressOfSymbol("JNI_CreateJavaVM");
+  JNI_CreateJavaVM = (jint(*)(JavaVM **, void **, void *))fn_ptr;
+
+  // Construct a VM
+  jsize nVMs = -1;
+  jint res = (*JNI_GetCreatedJavaVMs)(NULL, 0, &nVMs);
+  if (res != JNI_OK) {
+    printf("Failed to get Java VM count\n");
+    delete options;
+    return;
+  }
+
+  if (nVMs > 0) {
+    JavaVM **buffer = new JavaVM *[nVMs];
+    res = (*JNI_GetCreatedJavaVMs)(buffer, nVMs, &nVMs);
+    if (res != JNI_OK) {
+      printf("Failed to locate existing Java VM %x\n", res);
+      delete options;
+      return;
+    }
+    m_vm = buffer[0];
+    m_vm->GetEnv((void **)&m_env, JNI_VERSION_1_8);
+  } else {
+    m_vm = NULL;
+    m_env = NULL;
+    // FYI: This throws SIGSEGV but seems to not be normal for JVM startup
+    res = (*JNI_CreateJavaVM)(&m_vm, (void **)&m_env, &vm_args);
+    if (res != JNI_OK) {
+      printf("Failed to create Java VM %x\n", res);
+      delete options;
+      return;
+    }
+    res = (*JNI_GetCreatedJavaVMs)(NULL, 0, &nVMs);
+  }
+  delete options;
+
+  assert(m_env);
+}
+
+Java::~Java() {
+
+  assert(m_env);
+  // Shutdown the VM.
+  /// m_vm->DestroyJavaVM();
+}
+
+llvm::Error Java::Init(lldb::user_id_t debugger_id) {
+
+  m_vm->AttachCurrentThread((void **)&m_env, NULL);
+  dbg_id = debugger_id;
+
+  interpreter_cls = m_env->FindClass("SWIG/LldbScriptInterpreter");
+  if (interpreter_cls == nullptr) {
+    return createStringError(llvm::inconvertibleErrorCode(),
+                             "Failed to find 'LldbScriptInterpreter' class");
+  }
+
+  jint jid = (jint)debugger_id;
+  jstring jstr = m_env->NewStringUTF(swigJarPath);
+  jmethodID mid =
+      m_env->GetMethodID(interpreter_cls, "<init>", "(ILjava/lang/String;)V");
+  if (mid == nullptr) {
+    return createStringError(llvm::inconvertibleErrorCode(),
+                             "Failed to find <init> function");
+  }
+
+  interpreter = m_env->NewObject(interpreter_cls, mid, jid, jstr);
+  return llvm::Error::success();
+}
+
+llvm::Error Java::Test(llvm::StringRef buffer) {
+
+  m_vm->AttachCurrentThread((void **)&m_env, NULL);
+
+  if (llvm::Error e = Java::Init(dbg_id)) {
+    return e;
+  }
+  if (llvm::Error e = Run("/main")) {
+    return e;
+  }
+  if (llvm::Error e = Run(buffer.data())) {
+    return e;
+  }
+  if (llvm::Error e = Run("/close")) {
+    return e;
+  }
+  if (llvm::Error e = Run("/run")) {
+    return e;
+  }
+
+  return llvm::Error::success();
+}
+
+llvm::Error Java::Run(llvm::StringRef buffer) {
+
+  if (llvm::Error e = Run(buffer.data())) {
+    return e;
+  }
+  return llvm::Error::success();
+}
+
+llvm::Error Java::Run(const char *data) {
+
+  m_vm->AttachCurrentThread((void **)&m_env, NULL);
+
+  jmethodID midRunCmd =
+      m_env->GetMethodID(interpreter_cls, "runCmd", "(Ljava/lang/String;)Z");
+  if (midRunCmd == NULL) {
+    return createStringError(llvm::inconvertibleErrorCode(),
+                             "Failed to find 'runCmd' function");
+  }
+
+  jstring jstr = m_env->NewStringUTF(data);
+  jboolean res = m_env->CallBooleanMethod(interpreter, midRunCmd, jstr);
+  if (!(bool)res) {
+    return createStringError(llvm::inconvertibleErrorCode(),
+                             llvm::formatv("Failed to execute '{0}'", data));
+  }
+  return llvm::Error::success();
+}
+
+llvm::Error Java::RegisterBreakpointCallback(void *baton, const char *body,
+                                             bool existing) {
+
+  m_vm->AttachCurrentThread((void **)&m_env, NULL);
+
+  if (interpreter_cls == NULL) {
+    if (llvm::Error e = Java::Init(dbg_id)) {
+      return e;
+    }
+  }
+
+  if (!existing) {
+    std::string baton_str = llvm::formatv("/bpt {0}", baton);
+    if (llvm::Error e = Run(baton_str.c_str()))
+      return e;
+    if (llvm::Error e = Run("/init"))
+      return e;
+    if (llvm::Error e = Run(body))
+      return e;
+    if (strstr(body, "return ") == NULL) {
+      if (llvm::Error e = Run("return false;"))
+        return e;
+    }
+    if (llvm::Error e = Run("/close"))
+      return e;
+    if (llvm::Error e = Run("/compile"))
+      return e;
+  } else {
+    std::string baton_str = llvm::formatv("/register {0} {1}", baton, body);
+    if (llvm::Error e = Run(baton_str.c_str()))
+      return e;
+  }
+
+  return llvm::Error::success();
+}
+
+llvm::Expected<bool>
+Java::CallBreakpointCallback(void *baton, lldb::StackFrameSP stop_frame_sp,
+                             lldb::BreakpointLocationSP bp_loc_sp,
+                             StructuredData::ObjectSP extra_args_sp) {
+
+  m_vm->AttachCurrentThread((void **)&m_env, NULL);
+
+  auto *extra_args_impl = [&]() -> StructuredDataImpl * {
+    if (extra_args_sp == nullptr)
+      return nullptr;
+    auto *extra_args_impl = new StructuredDataImpl();
+    extra_args_impl->SetObjectSP(extra_args_sp);
+    return extra_args_impl;
+  }();
+
+  return LLDBSwigJavaBreakpointCallbackFunction(m_env, baton, dbg_id,
+                                                swigJarPath, stop_frame_sp,
+                                                bp_loc_sp, extra_args_impl);
+}
+
+llvm::Error Java::RegisterWatchpointCallback(void *baton, const char *body,
+                                             bool existing) {
+
+  if (interpreter_cls == NULL) {
+    if (llvm::Error e = Java::Init(dbg_id)) {
+      return e;
+    }
+  }
+
+  m_vm->AttachCurrentThread((void **)&m_env, NULL);
+
+  if (!existing) {
+    std::string baton_str = llvm::formatv("/wpt {0}", baton);
+    if (llvm::Error e = Run(baton_str.c_str()))
+      return e;
+    if (llvm::Error e = Run("/init"))
+      return e;
+    if (llvm::Error e = Run(body))
+      return e;
+    if (strcmp(body, "return ") < 0) {
+      if (llvm::Error e = Run("return false;"))
+        return e;
+    }
+    if (llvm::Error e = Run("/close"))
+      return e;
+    if (llvm::Error e = Run("/compile"))
+      return e;
+  } else {
+    std::string baton_str = llvm::formatv("/register {0} {1}", baton, body);
+    if (llvm::Error e = Run(baton_str.c_str()))
+      return e;
+  }
+
+  return llvm::Error::success();
+}
+
+llvm::Expected<bool>
+Java::CallWatchpointCallback(void *baton, lldb::StackFrameSP stop_frame_sp,
+                             lldb::WatchpointSP wp_sp) {
+
+  m_vm->AttachCurrentThread((void **)&m_env, NULL);
+
+  return LLDBSwigJavaWatchpointCallbackFunction(
+      m_env, baton, dbg_id, swigJarPath, stop_frame_sp, wp_sp);
+}
+
+llvm::Error Java::CheckSyntax(llvm::StringRef buffer) {
+  return llvm::Error::success();
+}
+
+llvm::Error Java::LoadModule(llvm::StringRef filename) {
+
+  if (interpreter_cls == NULL) {
+    if (llvm::Error e = Java::Init(dbg_id)) {
+      return e;
+    }
+  }
+
+  FileSpec file(filename);
+  if (!FileSystem::Instance().Exists(file)) {
+    return llvm::make_error<llvm::StringError>("invalid path",
+                                               llvm::inconvertibleErrorCode());
+  }
+
+  ConstString module_extension = file.GetFileNameExtension();
+  if (module_extension != ".java") {
+    return llvm::make_error<llvm::StringError>("invalid extension",
+                                               llvm::inconvertibleErrorCode());
+  }
+
+  std::string import_str = llvm::formatv("/import {0}", filename);
+  if (llvm::Error e = Run(import_str.c_str())) {
+    return e;
+  }
+
+  return llvm::Error::success();
+}
+
+llvm::Error Java::ChangeIO(FILE *out, FILE *err) {
+  assert(out != nullptr);
+  assert(err != nullptr);
+
+  if (interpreter_cls == NULL) {
+    return llvm::Error::success();
+  }
+
+  jmethodID midChangeIOCmd =
+      m_env->GetMethodID(interpreter_cls, "runChangeIO", "(JJ)Z");
+  if (midChangeIOCmd == NULL) {
+    return createStringError(llvm::inconvertibleErrorCode(),
+                             "Failed to find 'runChangeIO' function");
+  }
+
+  FileSP fout = std::make_shared<NativeFile>(out, false);
+  FileSP ferr = std::make_shared<NativeFile>(err, false);
+  jlong outPtr = jlong(&fout);
+  jlong errPtr = jlong(&ferr);
+  m_env->CallBooleanMethod(interpreter, midChangeIOCmd, outPtr, errPtr);
+  return llvm::Error::success();
+}
+
+llvm::Error makeError(const char *text) {
+  printf("makeError %s\n", text);
+  llvm::Error e = llvm::make_error<llvm::StringError>(
+      llvm::formatv("{0}\n", text), llvm::inconvertibleErrorCode());
+  return e;
+}
+
+llvm::Error Java::RunError(llvm::StringRef buffer) {
+
+  llvm::Error e = llvm::make_error<llvm::StringError>(
+      llvm::formatv("{0}\n", buffer.data()), llvm::inconvertibleErrorCode());
+  return std::move(e);
+}
+
+bool loadLibJVM() {
+
+#if defined(_WIN32)
+  const char *Suffix = ".dll";
+#elif defined(__APPLE__)
+  const char *Suffix = ".dylib";
+#else
+  const char *Suffix = ".so";
+#endif
+
+  const char *ldLibraryPath = ::getenv("JAVA_HOME");
+  std::string err = llvm::formatv("");
+
+  std::string jvmPath =
+      llvm::formatv("{0}/lib/server/libjvm{1}", ldLibraryPath, Suffix);
+  sys::DynamicLibrary::LoadLibraryPermanently(jvmPath.c_str(), &err);
+  if (strlen(err.c_str()) == 0) {
+    return true;
+  }
+
+  printf("Failed to find 'libjvm in %s/lib/server\n", ldLibraryPath);
+  return false;
+}
Index: lldb/source/Plugins/ScriptInterpreter/Java/CMakeLists.txt
===================================================================
--- /dev/null
+++ lldb/source/Plugins/ScriptInterpreter/Java/CMakeLists.txt
@@ -0,0 +1,17 @@
+find_package(Java REQUIRED)
+
+add_lldb_library(lldbPluginScriptInterpreterJava PLUGIN
+  Java.cpp
+  ScriptInterpreterJava.cpp
+
+  LINK_LIBS
+    lldbCore
+    lldbInterpreter
+  )
+
+string(TOLOWER ${CMAKE_SYSTEM_NAME} LC_CMAKE_SYSTEM_NAME)
+target_include_directories(lldbPluginScriptInterpreterJava PUBLIC ${JAVA_INCLUDE_DIR})
+target_include_directories(lldbPluginScriptInterpreterJava PUBLIC ${JAVA_INCLUDE_DIR}/${LC_CMAKE_SYSTEM_NAME})
+target_link_directories(lldbPluginScriptInterpreterJava PUBLIC ${JAVA_LIBRARIES})
+target_link_directories(lldbPluginScriptInterpreterJava PUBLIC ${JAVA_SERVER_LIB})
+
Index: lldb/source/Plugins/ScriptInterpreter/CMakeLists.txt
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/CMakeLists.txt
+++ lldb/source/Plugins/ScriptInterpreter/CMakeLists.txt
@@ -6,3 +6,9 @@
 if (LLDB_ENABLE_LUA)
   add_subdirectory(Lua)
 endif()
+
+if (LLDB_ENABLE_JAVA)
+  add_subdirectory(Java)
+endif()
+
+
Index: lldb/source/Interpreter/ScriptInterpreter.cpp
===================================================================
--- lldb/source/Interpreter/ScriptInterpreter.cpp
+++ lldb/source/Interpreter/ScriptInterpreter.cpp
@@ -64,6 +64,8 @@
     return "Python";
   case eScriptLanguageLua:
     return "Lua";
+  case eScriptLanguageJava:
+    return "Java";
   case eScriptLanguageUnknown:
     return "Unknown";
   }
@@ -99,6 +101,8 @@
     return eScriptLanguagePython;
   if (language.equals_insensitive(LanguageToString(eScriptLanguageLua)))
     return eScriptLanguageLua;
+  if (language.equals_insensitive(LanguageToString(eScriptLanguageJava)))
+    return eScriptLanguageJava;
   return eScriptLanguageUnknown;
 }
 
Index: lldb/source/Interpreter/OptionArgParser.cpp
===================================================================
--- lldb/source/Interpreter/OptionArgParser.cpp
+++ lldb/source/Interpreter/OptionArgParser.cpp
@@ -129,6 +129,8 @@
     return eScriptLanguagePython;
   if (s.equals_insensitive("lua"))
     return eScriptLanguageLua;
+  if (s.equals_insensitive("java"))
+    return eScriptLanguageJava;
   if (s.equals_insensitive("default"))
     return eScriptLanguageDefault;
   if (s.equals_insensitive("none"))
Index: lldb/source/Commands/CommandObjectWatchpointCommand.cpp
===================================================================
--- lldb/source/Commands/CommandObjectWatchpointCommand.cpp
+++ lldb/source/Commands/CommandObjectWatchpointCommand.cpp
@@ -41,6 +41,11 @@
         "lua",
         "Commands are in the Lua language.",
     },
+    {
+        eScriptLanguageJava,
+        "java",
+        "Commands are in the Java language.",
+    },
     {
         eSortOrderByName,
         "default-script",
@@ -337,6 +342,7 @@
         switch (m_script_language) {
         case eScriptLanguagePython:
         case eScriptLanguageLua:
+        case eScriptLanguageJava:
           m_use_script_language = true;
           break;
         case eScriptLanguageNone:
Index: lldb/source/Commands/CommandObjectScript.cpp
===================================================================
--- lldb/source/Commands/CommandObjectScript.cpp
+++ lldb/source/Commands/CommandObjectScript.cpp
@@ -31,6 +31,11 @@
         "lua",
         "Lua",
     },
+    {
+        eScriptLanguageJava,
+        "java",
+        "Java",
+    },
     {
         eScriptLanguageNone,
         "default",
Index: lldb/source/Commands/CommandObjectBreakpointCommand.cpp
===================================================================
--- lldb/source/Commands/CommandObjectBreakpointCommand.cpp
+++ lldb/source/Commands/CommandObjectBreakpointCommand.cpp
@@ -41,6 +41,11 @@
         "lua",
         "Commands are in the Lua language.",
     },
+    {
+        eScriptLanguageJava,
+        "java",
+        "Commands are in the Java language.",
+    },
     {
         eScriptLanguageDefault,
         "default-script",
@@ -305,6 +310,7 @@
         switch (m_script_language) {
         case eScriptLanguagePython:
         case eScriptLanguageLua:
+        case eScriptLanguageJava:
           m_use_script_language = true;
           break;
         case eScriptLanguageNone:
Index: lldb/source/API/liblldb.exports
===================================================================
--- lldb/source/API/liblldb.exports
+++ lldb/source/API/liblldb.exports
@@ -1,5 +1,9 @@
 _ZN4lldb*
 _ZNK4lldb*
+_ZN12lldb*
+_ZNK12lldb*
 init_lld*
 PyInit__lldb*
 luaopen_lldb*
+Java*
+
Index: lldb/source/API/liblldb-private.exports
===================================================================
--- lldb/source/API/liblldb-private.exports
+++ lldb/source/API/liblldb-private.exports
@@ -5,3 +5,5 @@
 init_lld*
 PyInit__lldb*
 luaopen_lldb*
+Java*
+
Index: lldb/source/API/SBDebugger.cpp
===================================================================
--- lldb/source/API/SBDebugger.cpp
+++ lldb/source/API/SBDebugger.cpp
@@ -736,6 +736,9 @@
   AddBoolConfigEntry(
       *config_up, "lua", LLDB_ENABLE_LUA,
       "A boolean value that indicates if lua support is enabled in LLDB");
+  AddBoolConfigEntry(
+      *config_up, "java", LLDB_ENABLE_JAVA,
+      "A boolean value that indicates if java support is enabled in LLDB");
   AddLLVMTargets(*config_up);
 
   SBStructuredData data;
Index: lldb/source/API/CMakeLists.txt
===================================================================
--- lldb/source/API/CMakeLists.txt
+++ lldb/source/API/CMakeLists.txt
@@ -19,6 +19,11 @@
   set(lldb_lua_wrapper ${lua_bindings_dir}/LLDBWrapLua.cpp)
 endif()
 
+if(LLDB_ENABLE_JAVA)
+  get_target_property(java_bindings_dir swig_wrapper_java BINARY_DIR)
+  set(lldb_java_wrapper ${java_bindings_dir}/SWIG/LLDBWrapJava.cpp)
+endif()
+
 add_lldb_library(liblldb SHARED ${option_framework}
   SBAddress.cpp
   SBAttachInfo.cpp
@@ -92,6 +97,7 @@
   SystemInitializerFull.cpp
   ${lldb_python_wrapper}
   ${lldb_lua_wrapper}
+  ${lldb_java_wrapper}
 
   LINK_LIBS
     lldbBase
@@ -160,6 +166,21 @@
   set_source_files_properties(${lldb_lua_wrapper} PROPERTIES GENERATED ON)
 endif()
 
+if(LLDB_ENABLE_JAVA)
+  add_dependencies(liblldb swig_wrapper_java)
+  target_include_directories(liblldb PRIVATE ${JAVA_INCLUDE_DIR})
+  target_include_directories(liblldb PRIVATE ${JAVA_INCLUDE_DIR}/darwin)
+
+  if (MSVC)
+    set_property(SOURCE ${lldb_java_wrapper} APPEND_STRING PROPERTY COMPILE_FLAGS " /W0")
+  else()
+    set_property(SOURCE ${lldb_java_wrapper} APPEND_STRING PROPERTY COMPILE_FLAGS " ")
+  endif()
+
+  set_source_files_properties(${lldb_java_wrapper} PROPERTIES GENERATED ON)
+endif()
+
+
 set_target_properties(liblldb
   PROPERTIES
   VERSION ${LLDB_VERSION}
Index: lldb/include/lldb/lldb-enumerations.h
===================================================================
--- lldb/include/lldb/lldb-enumerations.h
+++ lldb/include/lldb/lldb-enumerations.h
@@ -217,6 +217,7 @@
   eScriptLanguageNone = 0,
   eScriptLanguagePython,
   eScriptLanguageLua,
+  eScriptLanguageJava,
   eScriptLanguageUnknown,
   eScriptLanguageDefault = eScriptLanguagePython
 };
Index: lldb/include/lldb/Host/Config.h.cmake
===================================================================
--- lldb/include/lldb/Host/Config.h.cmake
+++ lldb/include/lldb/Host/Config.h.cmake
@@ -41,6 +41,8 @@
 
 #cmakedefine01 LLDB_ENABLE_LIBXML2
 
+#cmakedefine01 LLDB_ENABLE_JAVA
+
 #cmakedefine01 LLDB_ENABLE_LUA
 
 #cmakedefine01 LLDB_ENABLE_PYTHON
Index: lldb/include/lldb/Core/IOHandler.h
===================================================================
--- lldb/include/lldb/Core/IOHandler.h
+++ lldb/include/lldb/Core/IOHandler.h
@@ -55,6 +55,7 @@
     ProcessIO,
     PythonInterpreter,
     LuaInterpreter,
+    JavaInterpreter,
     PythonCode,
     Other
   };
Index: lldb/docs/resources/build.rst
===================================================================
--- lldb/docs/resources/build.rst
+++ lldb/docs/resources/build.rst
@@ -64,6 +64,8 @@
 +-------------------+------------------------------------------------------+--------------------------+
 | Lua               | Lua scripting                                        | ``LLDB_ENABLE_LUA``      |
 +-------------------+------------------------------------------------------+--------------------------+
+| Java              | Java scripting                                       | ``LLDB_ENABLE_JAVA``     |
++-------------------+------------------------------------------------------+--------------------------+
 
 Depending on your platform and package manager, one might run any of the
 commands below.
Index: lldb/cmake/modules/LLDBConfig.cmake
===================================================================
--- lldb/cmake/modules/LLDBConfig.cmake
+++ lldb/cmake/modules/LLDBConfig.cmake
@@ -56,6 +56,7 @@
 add_optional_dependency(LLDB_ENABLE_CURSES "Enable curses support in LLDB" CursesAndPanel CURSESANDPANEL_FOUND)
 add_optional_dependency(LLDB_ENABLE_LZMA "Enable LZMA compression support in LLDB" LibLZMA LIBLZMA_FOUND)
 add_optional_dependency(LLDB_ENABLE_LUA "Enable Lua scripting support in LLDB" LuaAndSwig LUAANDSWIG_FOUND)
+add_optional_dependency(LLDB_ENABLE_JAVA "Enable Java scripting support in LLDB" JavaAndSwig JAVAANDSWIG_FOUND)
 add_optional_dependency(LLDB_ENABLE_PYTHON "Enable Python scripting support in LLDB" PythonAndSwig PYTHONANDSWIG_FOUND)
 add_optional_dependency(LLDB_ENABLE_LIBXML2 "Enable Libxml 2 support in LLDB" LibXml2 LIBXML2_FOUND VERSION 2.8)
 
@@ -176,6 +177,9 @@
 check_cxx_compiler_flag("-Wno-vla-extension" CXX_SUPPORTS_NO_VLA_EXTENSION)
 append_if(CXX_SUPPORTS_NO_VLA_EXTENSION "-Wno-vla-extension" CMAKE_CXX_FLAGS)
 
+check_cxx_compiler_flag("-fexceptions" CXX_SUPPORTS_EXCEPTIONS)
+append_if(CXX_SUPPORTS_EXCEPTIONS "-fexceptions" CMAKE_CXX_FLAGS)
+
 # Disable MSVC warnings
 if( MSVC )
   add_definitions(
Index: lldb/cmake/modules/FindJavaAndSwig.cmake
===================================================================
--- /dev/null
+++ lldb/cmake/modules/FindJavaAndSwig.cmake
@@ -0,0 +1,45 @@
+#.rst:
+# FindJavaAndSwig
+# --------------
+#
+# Find Java and SWIG as a whole.
+
+if(JAVA_LIBRARIES AND JAVA_INCLUDE_DIR AND SWIG_EXECUTABLE)
+  set(JAVAANDSWIG_FOUND TRUE)
+else()
+  find_package(SWIG 3.0)
+  if (SWIG_FOUND)
+    find_package(Java 11.0)
+    find_package(JNI REQUIRED)
+    if(JAVA_FOUND AND SWIG_FOUND)
+      mark_as_advanced(
+        JAVA_LIBRARIES
+        JAVA_INCLUDE_DIR
+        SWIG_EXECUTABLE)
+    endif()
+  else()
+    message(STATUS "SWIG 3 or later is required for Java support in LLDB but could not be found")
+  endif()
+
+  if (DEFINED ENV{JAVA_HOME})
+    set(JAVA_LIBRARIES  $ENV{JAVA_HOME}/lib)
+    set(JAVA_SERVER_LIB  $ENV{JAVA_HOME}/lib/server)
+    set(JAVA_INCLUDE_DIR  $ENV{JAVA_HOME}/include)
+    set(JAVAC_EXECUTABLE  $ENV{JAVA_HOME}/bin/javac)
+    set(JAR_EXECUTABLE  $ENV{JAVA_HOME}/bin/jar)
+    include_directories(${JAVA_INCLUDE_DIR})
+    link_directories(${JAVA_SERVER_LIB})
+    set(JAVAANDSWIG_FOUND TRUE)
+  else()
+    message(STATUS "Please ensure the Environment variable JAVA_HOME has been set correctly")
+  endif()
+  
+  include(FindPackageHandleStandardArgs)
+  find_package_handle_standard_args(JavaAndSwig
+                                    FOUND_VAR
+                                      JAVAANDSWIG_FOUND
+                                    REQUIRED_VARS
+                                      JAVA_LIBRARIES
+                                      JAVA_INCLUDE_DIR
+                                      SWIG_EXECUTABLE)
+endif()
Index: lldb/bindings/java/java.swig
===================================================================
--- /dev/null
+++ lldb/bindings/java/java.swig
@@ -0,0 +1,25 @@
+/*
+   lldb.swig
+
+   This is the input file for SWIG, to create the appropriate C++ wrappers and
+   functions for various scripting languages, to enable them to call the
+   liblldb Script Bridge functions.
+*/
+
+%module lldb
+
+%include <std_string.i>
+%include "java-typemaps.swig"
+%include "macros.swig"
+%include "headers.swig"
+
+%{
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FormatVariadic.h"
+//#include "../bindings/java/java-swigsafecast.swig"
+using namespace lldb_private;
+using namespace lldb;
+%}
+
+%include "interfaces.swig"
+%include "java-wrapper.swig"
Index: lldb/bindings/java/java-wrapper.swig
===================================================================
--- /dev/null
+++ lldb/bindings/java/java-wrapper.swig
@@ -0,0 +1,121 @@
+%header %{
+
+%}
+
+%wrapper %{
+
+// This function is called from Java::CallBreakpointCallback
+SWIGEXPORT llvm::Expected<bool>
+LLDBSwigJavaBreakpointCallbackFunction
+(
+   JNIEnv *env,
+   void *baton,
+   lldb::user_id_t debugger_id,
+   const char * swigJarPath,
+   lldb::StackFrameSP stop_frame_sp,
+   lldb::BreakpointLocationSP bp_loc_sp,
+   StructuredDataImpl *extra_args_impl
+)
+{
+  //printf("entering LLDBSwigJavaBreakpointCallbackFunction %x %x\n", stop_frame_sp, bp_loc_sp);
+                                               
+  lldb::SBFrame sb_frame(stop_frame_sp);
+  lldb::SBBreakpointLocation sb_bp_loc(bp_loc_sp);
+
+  jclass interpreter_cls = env->FindClass("SWIG/LldbScriptInterpreter");
+  if (interpreter_cls == NULL) {
+    printf("Failed to find 'LldbScriptInterpreter' class");
+    return false;
+  }
+   
+  jint jid = (jint) debugger_id;
+  jstring jpath = env->NewStringUTF(swigJarPath);
+  jmethodID mid = env->GetMethodID(interpreter_cls, "<init>", "(ILjava/lang/String;)V");
+  if (mid == nullptr) {
+    printf("Failed to find '<init>' function");
+    return false;
+  }
+
+  jobject interpreter = env->NewObject(interpreter_cls, mid, jid, jpath);
+  if (interpreter == NULL) {
+    printf("Failed to find 'interpreter' for %ld %s", debugger_id, swigJarPath);
+    return false;
+  }
+
+  jmethodID midCallbackCmd = env->GetMethodID(interpreter_cls, "runBreakpointCallback", "(Ljava/lang/String;JJJ)Z");
+  if (midCallbackCmd == NULL) {
+    printf("Failed to find 'runBreakpointCallback' function");
+    return false;
+  }
+
+  lldb::SBStructuredData *extra_args = NULL;
+  if (extra_args_impl)
+     extra_args = new lldb::SBStructuredData(extra_args_impl);
+ 
+  std::string baton_str = llvm::formatv("{0}", baton);
+  jstring jstr = env->NewStringUTF(baton_str.c_str());
+  jlong framePtr = jlong(&sb_frame);
+  jlong bptPtr = jlong(&sb_bp_loc);
+  jlong argsPtr = jlong(extra_args);
+  jboolean ret = (jboolean) env->CallBooleanMethod(interpreter, midCallbackCmd, jstr, framePtr, bptPtr, argsPtr);
+  
+  if (extra_args_impl)
+    delete extra_args;   
+  return (bool) ret;
+}
+
+// This function is called from Java::CallWatchpointCallback
+SWIGEXPORT llvm::Expected<bool>
+LLDBSwigJavaWatchpointCallbackFunction
+(
+   JNIEnv *env,
+   void *baton,
+   lldb::user_id_t debugger_id,
+   const char * swigJarPath,
+   lldb::StackFrameSP stop_frame_sp,
+   lldb::WatchpointSP wp_sp
+)
+{
+  //printf("entering LLDBSwigJavaWatchpointCallbackFunction\n");
+  
+  lldb::SBFrame sb_frame(stop_frame_sp);
+  lldb::SBWatchpoint sb_wp(wp_sp);
+ 
+  jclass interpreter_cls = env->FindClass("SWIG/LldbScriptInterpreter");
+  if (interpreter_cls == NULL) {
+    printf("Failed to find 'LldbScriptInterpreter' class");
+    return false;
+  }
+   
+  jint jid = (jint) debugger_id;
+  jstring jpath = env->NewStringUTF(swigJarPath);
+  jmethodID mid = env->GetMethodID(interpreter_cls, "<init>", "(ILjava/lang/String;)V");
+  if (mid == nullptr) {
+    printf("Failed to find '<init>' function");
+    return false;
+  }
+
+  jobject interpreter = env->NewObject(interpreter_cls, mid, jid, jpath);
+  if (interpreter == NULL) {
+    printf("Failed to find 'interpreter' for %ld %s", debugger_id, swigJarPath);
+    return false;
+  }
+
+  jmethodID midCallbackCmd = env->GetMethodID(interpreter_cls, "runWatchpointCallback", 
+  	"(Ljava/lang/String;JJ)Z");
+  if (midCallbackCmd == NULL) {
+    printf("Failed to find 'runWatchpointCallback' function");
+    return false;
+  }
+
+  std::string baton_str = llvm::formatv("{0}", baton);
+  jstring jstr = env->NewStringUTF(baton_str.c_str());
+  jlong framePtr = jlong(&sb_frame);
+  jlong wptPtr = jlong(&sb_wp);
+  jboolean ret = (jboolean) env->CallBooleanMethod(interpreter, midCallbackCmd, jstr, framePtr, wptPtr);
+  
+  return (bool) ret;
+}
+
+
+%}
Index: lldb/bindings/java/java-typemaps.swig
===================================================================
--- /dev/null
+++ lldb/bindings/java/java-typemaps.swig
@@ -0,0 +1,18 @@
+%include <typemaps.i>
+%include <carrays.i>
+%include <various.i>
+
+%typemap(javabase) ByteArray "SWIGTYPE_p_void"
+%typemap(javabody) ByteArray %{
+  private long swigCPtr; // Minor bodge to work around private variable in parent
+  private boolean swigCMemOwn;
+  public $javaclassname(long cPtr, boolean cMemoryOwn) {
+    super(cPtr, cMemoryOwn);
+    this.swigCPtr = SWIGTYPE_p_void.getCPtr(this);
+    swigCMemOwn = cMemoryOwn;
+  }
+%}
+
+%array_class(jbyte, ByteArray);
+
+%apply char **STRING_ARRAY { char ** }
Index: lldb/bindings/java/java-swigsafecast.swig
===================================================================
--- /dev/null
+++ lldb/bindings/java/java-swigsafecast.swig
@@ -0,0 +1,27 @@
+template <typename SBClass>
+void
+PushSBClass (JNIEnv* L, SBClass* obj);
+
+void
+PushSBClass (JNIEnv* L, lldb::SBFrame* frame_sb)
+{
+   SWIG_NewPointerObj(L, frame_sb, SWIGTYPE_p_lldb__SBFrame, 0);
+}
+
+void
+PushSBClass (JNIEnv* L, lldb::SBBreakpointLocation* breakpoint_location_sb)
+{
+   SWIG_NewPointerObj(L, breakpoint_location_sb, SWIGTYPE_p_lldb__SBBreakpointLocation, 0);
+}
+
+void
+PushSBClass (JNIEnv* L, lldb::SBWatchpoint* watchpoint_sb)
+{
+   SWIG_NewPointerObj(L, watchpoint_sb, SWIGTYPE_p_lldb__SBWatchpoint, 0);
+}
+
+void
+PushSBClass (JNIEnv* L, lldb::SBStructuredData* structured_data_sb)
+{
+   SWIG_NewPointerObj(L, structured_data_sb, SWIGTYPE_p_lldb__SBStructuredData, 0);
+}
Index: lldb/bindings/java/SWIG/LldbScriptInterpreter.java
===================================================================
--- /dev/null
+++ lldb/bindings/java/SWIG/LldbScriptInterpreter.java
@@ -0,0 +1,411 @@
+package SWIG;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticCollector;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.ToolProvider;
+import jdk.jshell.JShell;
+import jdk.jshell.JShellException;
+import jdk.jshell.Snippet.Status;
+import jdk.jshell.SnippetEvent;
+
+public class LldbScriptInterpreter {
+
+  private int debuggerID;
+  private String tmpDirPath;
+  private JavaCompiler compiler;
+  StandardJavaFileManager fm;
+  PrintWriter writer;
+  DiagnosticCollector<JavaFileObject> diagnostics;
+  List<String> options = new ArrayList<>();
+
+  StringWriter sw;
+  PrintWriter pw;
+  private boolean initialized = false;
+  private String className, methodName, methodSig;
+  private static Map<String, String> map = new HashMap<>();
+  private static boolean shellEnabled = false;
+  private static JShell shell;
+
+  public LldbScriptInterpreter(int debuggerID, String swigPath) {
+    this.debuggerID = debuggerID;
+    tmpDirPath = System.getProperty("java.io.tmpdir");
+    compiler = ToolProvider.getSystemJavaCompiler();
+    fm = compiler.getStandardFileManager(null, null, null);
+    writer = new PrintWriter(new StringWriter());
+    diagnostics = new DiagnosticCollector<JavaFileObject>();
+
+    options.add("-g");
+    options.add("-d");
+    options.add(tmpDirPath);
+    options.add("-classpath");
+    options.add(System.getProperty("java.class.path") + File.pathSeparator +
+                tmpDirPath + File.pathSeparator + swigPath);
+    options.add("-proc:none");
+    System.loadLibrary("lldb");
+  }
+
+  public boolean runCmd(String cmd) {
+
+    if (!initialized) {
+      initializeWriter();
+    }
+    if (!cmd.startsWith("/")) {
+      return evalAsCode(cmd);
+    }
+
+    // Might be a special command
+    boolean compile = cmd.startsWith("/ru") || cmd.startsWith("/co");
+    boolean execute =
+        cmd.startsWith("/ru") || cmd.startsWith("/x") || cmd.startsWith("/ex");
+    if (compile || execute) {
+      if (compile) {
+        if (!runCompile()) {
+          System.err.println("compile failed");
+          return false;
+        }
+      }
+      initialized = false;
+      if (execute && map.containsValue(className)) {
+        return runMain(cmd);
+      }
+      return true;
+    } else if (cmd.equals("/reset")) {
+      initialized = false;
+      return true;
+    } else if (cmd.startsWith("/register")) {
+      return runRegister(cmd);
+    } else if (cmd.startsWith("/main") || cmd.startsWith("/bpt") ||
+               cmd.startsWith("/wpt")) {
+      return buildPrologue(cmd);
+    } else if (cmd.equals("/init")) {
+      return buildContext();
+    } else if (cmd.equals("/close")) {
+      return buildEpilogue();
+    } else if (cmd.startsWith("/import")) {
+      return runImport(cmd);
+    } else if (cmd.startsWith("/shell")) {
+      return runShell(cmd);
+    }
+    return evalAsCode(cmd);
+  }
+
+  private void initializeWriter() {
+    sw = new StringWriter();
+    pw = new PrintWriter(sw);
+    initialized = true;
+  }
+
+  private boolean evalAsCode(String cmd) {
+    if (shellEnabled || cmd.startsWith("/shell")) {
+      execShell(cmd);
+    } else {
+      pw.println(cmd);
+    }
+    return true;
+  }
+
+  private boolean buildPrologue(String cmd) {
+    int randomInt = (int)(Math.random() * 1000);
+    className = "LLDB_" + randomInt;
+    methodName = cmd.startsWith("/main") ? "main" : "callback";
+    methodSig = cmd.startsWith("/main") ? "void" : "boolean";
+    String[] split = cmd.split(" ");
+    if (split.length > 1) {
+      map.put(split[1], className);
+    }
+    pw.println("import SWIG.*;");
+    pw.println("public class " + className + " {");
+    pw.print("  public static " + methodSig + " " + methodName);
+    if (cmd.startsWith("/main")) {
+      pw.println("(String args[]) {");
+      return true;
+    } else if (cmd.startsWith("/bpt")) {
+      pw.println(
+          "(SBFrame _frame, SBBreakpointLocation loc, SBStructuredData args) {");
+      return true;
+    } else if (cmd.startsWith("/wpt")) {
+      pw.println("(SBFrame _frame, SBWatchpoint wp) {");
+      return true;
+    }
+    return false;
+  }
+
+  private boolean buildContext() {
+    pw.println("SBDebugger debugger = SBDebugger.FindDebuggerWithID(" +
+               debuggerID + ");");
+    pw.println("SBTarget target = debugger.GetSelectedTarget();");
+    pw.println("SBProcess process = target.GetProcess();");
+    pw.println("SBThread thread = process.GetSelectedThread();");
+    pw.println("SBFrame frame = thread.GetSelectedFrame();");
+    return true;
+  }
+
+  private boolean buildEpilogue() {
+    pw.println("  }");
+    pw.println("}");
+    pw.close();
+    return true;
+  }
+
+  public boolean runCompile() {
+    if (!execCompile(className, sw.toString())) {
+      return false;
+    }
+    map.put(className, className);
+    return true;
+  }
+
+  private boolean runMain(String cmd) {
+    String[] split = cmd.split(" ");
+    if (split.length > 1) {
+      if (map.containsKey(split[1])) {
+        className = map.get(split[1]);
+      } else {
+        System.err.println("Class for " + split[1] + " not found!");
+        return false;
+      }
+    }
+    execMain(className);
+    return true;
+  }
+
+  private boolean runRegister(String cmd) {
+    String[] split = cmd.split(" ");
+    if (split.length > 2) {
+      String x = map.get(split[2]);
+      if (x == null) {
+        System.err.println("Map does not contain " + split[2]);
+        System.err.println(map.keySet());
+        return false;
+      }
+      map.put("LLDB_" + split[1], x);
+    }
+    return true;
+  }
+
+  private boolean runImport(String cmd) {
+    String[] split = cmd.split(" ");
+    File f = new File(split[1]);
+    className = f.getName();
+    try {
+      BufferedReader in = new BufferedReader(new InputStreamReader(
+          new FileInputStream(f), StandardCharsets.UTF_8));
+      String line;
+      while ((line = in.readLine()) != null) {
+        pw.println(line);
+      }
+    } catch (IOException e) {
+      e.printStackTrace();
+      return false;
+    }
+    pw.close();
+    if (!execCompile(className, sw.toString())) {
+      return false;
+    }
+    map.put(className, className);
+    return true;
+  }
+
+  private boolean runShell(String cmd) {
+    String[] split = cmd.split(" ");
+    if (split.length > 1) {
+      if (split[1].equals("off")) {
+        shellEnabled = false;
+        return true;
+      }
+      cmd = cmd.substring(6);
+    }
+    if (!shellEnabled) {
+      shell = JShell.builder().build();
+    }
+    shellEnabled = true;
+    execShell(cmd);
+    return true;
+  }
+
+  public boolean runBreakpointCallback(String baton, long framePtr,
+                                       long bpLocPtr, long extraArgsPtr) {
+    SBFrame frame = framePtr == 0 ? null : new SBFrame(framePtr, false);
+    SBBreakpointLocation loc =
+        bpLocPtr == 0 ? null : new SBBreakpointLocation(framePtr, false);
+    SBStructuredData extraArgs =
+        extraArgsPtr == 0 ? null : new SBStructuredData(extraArgsPtr, false);
+    return execBreakpointCallback(baton, frame, loc, extraArgs);
+  }
+
+  public boolean runWatchpointCallback(String baton, long framePtr,
+                                       long wpLocPtr) {
+    SBFrame frame = framePtr == 0 ? null : new SBFrame(framePtr, false);
+    SBWatchpoint wp = wpLocPtr == 0 ? null : new SBWatchpoint(framePtr, false);
+    return execWatchpointCallback(baton, frame, wp);
+  }
+
+  public boolean runChangeIO(long outPtr, long errPtr) {
+    SWIGTYPE_p_std__shared_ptrT_lldb_private__File_t out =
+        new SWIGTYPE_p_std__shared_ptrT_lldb_private__File_t(outPtr, true);
+    SWIGTYPE_p_std__shared_ptrT_lldb_private__File_t err =
+        new SWIGTYPE_p_std__shared_ptrT_lldb_private__File_t(errPtr, true);
+    SBDebugger debugger = SBDebugger.FindDebuggerWithID(debuggerID);
+    debugger.SetOutputFile(out);
+    debugger.SetErrorFile(err);
+    return true;
+  }
+
+  public boolean execCompile(String fname, String code) {
+    JavaFileObject file = new JavaSourceFromString(fname, code);
+
+    List<? extends JavaFileObject> files = Arrays.asList(file);
+    JavaCompiler.CompilationTask task =
+        compiler.getTask(writer, fm, diagnostics, options, null, files);
+    if (!task.call()) {
+      System.err.println("Unexpected compilation failure:");
+      System.err.println(code);
+      StringBuffer sb = new StringBuffer();
+      for (Diagnostic<?> diagnostic : diagnostics.getDiagnostics()) {
+        sb.append("Error on line " + diagnostic.getLineNumber() + " in " +
+                  diagnostic);
+      }
+      System.err.println(sb.toString());
+      return false;
+    }
+    return true;
+  }
+
+  public void execMain(String fileName) {
+    try {
+      File x = new File(tmpDirPath);
+      URLClassLoader classLoader = URLClassLoader.newInstance(
+          new URL[] {x.toURI().toURL()}); // root is path to class file
+      Class<?> cls = Class.forName(fileName, true, classLoader);
+      cls.getDeclaredMethod("main", new Class[] {String[].class})
+          .invoke(null, new Object[] {null});
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+  }
+
+  public boolean execBreakpointCallback(String batonName, SBFrame frame,
+                                        SBBreakpointLocation loc,
+                                        SBStructuredData args) {
+    Boolean retval = false;
+    String className = map.containsKey(batonName)
+                           ? map.get(batonName)
+                           : map.get("LLDB_" + batonName);
+    if (className == null) {
+      System.err.println("Class for " + batonName + " not found!");
+      return false;
+    }
+    try {
+      File x = new File(tmpDirPath);
+      URLClassLoader classLoader = URLClassLoader.newInstance(
+          new URL[] {x.toURI().toURL()}); // root is path to class file
+      Class<?> cls = Class.forName(className, true, classLoader);
+      Object ret = cls.getDeclaredMethod(
+                          "callback", new Class[] {SBFrame.class,
+                                                   SBBreakpointLocation.class,
+                                                   SBStructuredData.class})
+                       .invoke(null, new Object[] {frame, loc, args});
+      retval = (Boolean)ret;
+      return retval;
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    return retval;
+  }
+
+  public boolean execWatchpointCallback(String batonName, SBFrame frame,
+                                        SBWatchpoint wp) {
+    Boolean retval = false;
+    String className = map.containsKey(batonName)
+                           ? map.get(batonName)
+                           : map.get("LLDB_" + batonName);
+    if (className == null) {
+      System.err.println("Class for " + batonName + " not found!");
+      return false;
+    }
+    try {
+      File x = new File(tmpDirPath);
+      URLClassLoader classLoader = URLClassLoader.newInstance(
+          new URL[] {x.toURI().toURL()}); // root is path to class file
+      Class<?> cls = Class.forName(className, true, classLoader);
+      Object ret =
+          cls.getDeclaredMethod("callback",
+                                new Class[] {SBFrame.class, SBWatchpoint.class})
+              .invoke(null, new Object[] {frame, wp});
+      retval = (Boolean)ret;
+      return retval;
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    return retval;
+  }
+
+  public String execShell(String shellCmd) {
+    List<SnippetEvent> events = shell.eval(shellCmd);
+    String retval = "";
+    for (SnippetEvent event : events) {
+      Status status = event.status();
+      switch (status) {
+      case OVERWRITTEN:
+        retval += "OVERWRITING: ";
+      case VALID:
+        retval += event.value() + "\n";
+        break;
+      case RECOVERABLE_DEFINED:
+      case RECOVERABLE_NOT_DEFINED:
+      case DROPPED:
+      case REJECTED:
+      case NONEXISTENT:
+        retval += event.toString() + "\n";
+        break;
+      }
+      JShellException exception = event.exception();
+      if (exception != null) {
+        retval += "EXC: " + exception + "\n";
+        StackTraceElement[] stackTrace = exception.getStackTrace();
+        for (int i = 0; i < stackTrace.length; i++) {
+          System.err.println(stackTrace[i].getMethodName() + ":" +
+                             stackTrace[i].getLineNumber());
+        }
+      }
+    }
+    System.out.print(retval);
+    return retval;
+  }
+
+  class JavaSourceFromString extends SimpleJavaFileObject {
+    final String code;
+
+    JavaSourceFromString(String name, String code) {
+      super(URI.create("string:///" + name.replace('.', '/') +
+                       Kind.SOURCE.extension),
+            Kind.SOURCE);
+      this.code = code;
+    }
+
+    @Override
+    public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+      return code;
+    }
+  }
+}
Index: lldb/bindings/java/CMakeLists.txt
===================================================================
--- /dev/null
+++ lldb/bindings/java/CMakeLists.txt
@@ -0,0 +1,27 @@
+FILE(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/SWIG")
+add_custom_command(
+  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/SWIG/LLDBWrapJava.cpp
+  DEPENDS ${SWIG_SOURCES}
+  DEPENDS ${SWIG_INTERFACES}
+  DEPENDS ${SWIG_HEADERS}
+  COMMAND ${SWIG_EXECUTABLE}
+      ${SWIG_COMMON_FLAGS}
+      -I${CMAKE_CURRENT_SOURCE_DIR}
+      -java
+      -package SWIG
+      -c++
+      -outdir ${CMAKE_CURRENT_BINARY_DIR}/SWIG
+      -o ${CMAKE_CURRENT_BINARY_DIR}/SWIG/LLDBWrapJava.cpp
+      ${CMAKE_CURRENT_SOURCE_DIR}/java.swig
+  VERBATIM
+  COMMENT "Building LLDB Java wrapper")
+
+add_custom_target(swig_wrapper_java ALL DEPENDS
+  ${CMAKE_CURRENT_BINARY_DIR}/SWIG/LLDBWrapJava.cpp
+)
+
+FILE(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/classes")
+add_custom_command(TARGET swig_wrapper_java POST_BUILD
+  COMMAND "${JAVAC_EXECUTABLE}" -d ${CMAKE_CURRENT_BINARY_DIR}/classes ${CMAKE_CURRENT_SOURCE_DIR}/SWIG/*.java
+  COMMAND ${JAR_EXECUTABLE} -cfM SWIG.jar -C ${CMAKE_CURRENT_BINARY_DIR}/classes .
+)
Index: lldb/bindings/CMakeLists.txt
===================================================================
--- lldb/bindings/CMakeLists.txt
+++ lldb/bindings/CMakeLists.txt
@@ -38,3 +38,8 @@
 if (LLDB_ENABLE_LUA)
   add_subdirectory(lua)
 endif()
+
+if (LLDB_ENABLE_JAVA)
+  add_subdirectory(java)
+endif()
+
Index: lldb/CMakeLists.txt
===================================================================
--- lldb/CMakeLists.txt
+++ lldb/CMakeLists.txt
@@ -58,7 +58,7 @@
     CACHE STRING "Path where Lua modules are installed, relative to install prefix")
 endif ()
 
-if (LLDB_ENABLE_PYTHON OR LLDB_ENABLE_LUA)
+if (LLDB_ENABLE_PYTHON OR LLDB_ENABLE_LUA OR LLDB_ENABLE_JAVA)
   add_subdirectory(bindings)
 endif ()
 
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to