[Lldb-commits] [lldb] [lldb] add javascript scripting support (PR #165805)
Chad Smith via lldb-commits
lldb-commits at lists.llvm.org
Sat Nov 1 20:09:58 PDT 2025
https://github.com/cs01 updated https://github.com/llvm/llvm-project/pull/165805
>From e0622049d1670b4381ff9f17d8b9c79b5694ec7d Mon Sep 17 00:00:00 2001
From: Chad Smith <cssmith at fb.com>
Date: Tue, 28 Oct 2025 15:33:33 -0700
Subject: [PATCH] add javascript support
---
lldb/CMakeLists.txt | 12 +-
lldb/bindings/CMakeLists.txt | 4 +
lldb/bindings/javascript/CMakeLists.txt | 69 +++
.../javascript/javascript-swigsafecast.swig | 8 +
.../javascript/javascript-typemaps.swig | 48 ++
.../javascript/javascript-wrapper.swig | 12 +
lldb/bindings/javascript/javascript.swig | 30 ++
lldb/cmake/modules/FindV8.cmake | 71 +++
lldb/cmake/modules/LLDBConfig.cmake | 1 +
lldb/docs/index.rst | 5 +-
lldb/docs/resources/build.rst | 2 +
lldb/docs/use/javascript-reference.md | 263 +++++++++++
.../Interpreter/CommandOptionArgumentTable.h | 5 +
lldb/include/lldb/lldb-enumerations.h | 13 +-
.../CommandObjectBreakpointCommand.cpp | 1 +
.../CommandObjectWatchpointCommand.cpp | 1 +
lldb/source/Core/Debugger.cpp | 5 +
lldb/source/Interpreter/ScriptInterpreter.cpp | 4 +
.../Plugins/ScriptInterpreter/CMakeLists.txt | 4 +
.../JavaScript/CMakeLists.txt | 40 ++
.../JavaScript/JavaScript.cpp | 435 ++++++++++++++++++
.../ScriptInterpreter/JavaScript/JavaScript.h | 107 +++++
.../JavaScript/SWIGJavaScriptBridge.h | 51 ++
.../ScriptInterpreterJavaScript.cpp | 278 +++++++++++
.../JavaScript/ScriptInterpreterJavaScript.h | 121 +++++
25 files changed, 1586 insertions(+), 4 deletions(-)
create mode 100644 lldb/bindings/javascript/CMakeLists.txt
create mode 100644 lldb/bindings/javascript/javascript-swigsafecast.swig
create mode 100644 lldb/bindings/javascript/javascript-typemaps.swig
create mode 100644 lldb/bindings/javascript/javascript-wrapper.swig
create mode 100644 lldb/bindings/javascript/javascript.swig
create mode 100644 lldb/cmake/modules/FindV8.cmake
create mode 100644 lldb/docs/use/javascript-reference.md
create mode 100644 lldb/source/Plugins/ScriptInterpreter/JavaScript/CMakeLists.txt
create mode 100644 lldb/source/Plugins/ScriptInterpreter/JavaScript/JavaScript.cpp
create mode 100644 lldb/source/Plugins/ScriptInterpreter/JavaScript/JavaScript.h
create mode 100644 lldb/source/Plugins/ScriptInterpreter/JavaScript/SWIGJavaScriptBridge.h
create mode 100644 lldb/source/Plugins/ScriptInterpreter/JavaScript/ScriptInterpreterJavaScript.cpp
create mode 100644 lldb/source/Plugins/ScriptInterpreter/JavaScript/ScriptInterpreterJavaScript.h
diff --git a/lldb/CMakeLists.txt b/lldb/CMakeLists.txt
index e3b72e94d4beb..a852e581160b5 100644
--- a/lldb/CMakeLists.txt
+++ b/lldb/CMakeLists.txt
@@ -95,7 +95,7 @@ if (LLDB_ENABLE_LUA)
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_JAVASCRIPT)
add_subdirectory(bindings)
endif ()
@@ -150,6 +150,16 @@ if (LLDB_ENABLE_LUA)
finish_swig_lua("lldb-lua" "${lldb_lua_bindings_dir}" "${LLDB_LUA_CPATH}")
endif()
+if (LLDB_ENABLE_JAVASCRIPT)
+ if(LLDB_BUILD_FRAMEWORK)
+ set(lldb_javascript_target_dir "${LLDB_FRAMEWORK_ABSOLUTE_BUILD_DIR}/LLDB.framework/Resources/JavaScript")
+ else()
+ set(lldb_javascript_target_dir "${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib/javascript")
+ endif()
+ get_target_property(lldb_javascript_bindings_dir swig_wrapper_javascript BINARY_DIR)
+ finish_swig_javascript("lldb-javascript" "${lldb_javascript_bindings_dir}" "${lldb_javascript_target_dir}")
+endif()
+
set(LLDB_INCLUDE_UNITTESTS ON)
if (NOT TARGET llvm_gtest)
set(LLDB_INCLUDE_UNITTESTS OFF)
diff --git a/lldb/bindings/CMakeLists.txt b/lldb/bindings/CMakeLists.txt
index bec694e43bd7b..984614a1238aa 100644
--- a/lldb/bindings/CMakeLists.txt
+++ b/lldb/bindings/CMakeLists.txt
@@ -57,3 +57,7 @@ endif()
if (LLDB_ENABLE_LUA)
add_subdirectory(lua)
endif()
+
+if (LLDB_ENABLE_JAVASCRIPT)
+ add_subdirectory(javascript)
+endif()
diff --git a/lldb/bindings/javascript/CMakeLists.txt b/lldb/bindings/javascript/CMakeLists.txt
new file mode 100644
index 0000000000000..7356625f71a90
--- /dev/null
+++ b/lldb/bindings/javascript/CMakeLists.txt
@@ -0,0 +1,69 @@
+add_custom_command(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/LLDBWrapJavaScript.cpp
+ DEPENDS ${SWIG_SOURCES}
+ DEPENDS ${SWIG_INTERFACES}
+ DEPENDS ${SWIG_HEADERS}
+ DEPENDS lldb-sbapi-dwarf-enums
+ COMMAND ${SWIG_EXECUTABLE}
+ ${SWIG_COMMON_FLAGS}
+ -I${CMAKE_CURRENT_SOURCE_DIR}
+ -javascript
+ -v8
+ -w503
+ -outdir ${CMAKE_CURRENT_BINARY_DIR}
+ -o ${CMAKE_CURRENT_BINARY_DIR}/LLDBWrapJavaScript.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/javascript.swig
+ VERBATIM
+ COMMENT "Building LLDB JavaScript wrapper")
+
+add_custom_target(swig_wrapper_javascript ALL DEPENDS
+ ${CMAKE_CURRENT_BINARY_DIR}/LLDBWrapJavaScript.cpp
+)
+
+function(create_javascript_package swig_target working_dir pkg_dir)
+ cmake_parse_arguments(ARG "NOINIT" "" "FILES" ${ARGN})
+ add_custom_command(TARGET ${swig_target} POST_BUILD VERBATIM
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${pkg_dir}
+ WORKING_DIRECTORY ${working_dir})
+endfunction()
+
+function(finish_swig_javascript swig_target lldb_javascript_bindings_dir lldb_javascript_target_dir)
+ add_custom_target(${swig_target} ALL VERBATIM
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${lldb_javascript_target_dir}
+ DEPENDS swig_wrapper_javascript liblldb
+ COMMENT "LLDB JavaScript API")
+ if(LLDB_BUILD_FRAMEWORK)
+ set(LIBLLDB_SYMLINK_DEST "${LLDB_FRAMEWORK_ABSOLUTE_BUILD_DIR}/LLDB.framework/LLDB")
+ else()
+ set(LIBLLDB_SYMLINK_DEST "${LLVM_SHLIB_OUTPUT_INTDIR}/liblldb${CMAKE_SHARED_LIBRARY_SUFFIX}")
+ endif()
+ if(WIN32)
+ set(LIBLLDB_SYMLINK_OUTPUT_FILE "lldb.dll")
+ else()
+ set(LIBLLDB_SYMLINK_OUTPUT_FILE "lldb.so")
+ endif()
+ create_relative_symlink(${swig_target} ${LIBLLDB_SYMLINK_DEST}
+ ${lldb_javascript_target_dir} ${LIBLLDB_SYMLINK_OUTPUT_FILE})
+ set(lldb_javascript_library_target "${swig_target}-library")
+ add_custom_target(${lldb_javascript_library_target})
+ add_dependencies(${lldb_javascript_library_target} ${swig_target})
+
+ # Ensure we do the JavaScript post-build step when building lldb.
+ add_dependencies(lldb ${swig_target})
+
+ if(LLDB_BUILD_FRAMEWORK)
+ set(LLDB_JAVASCRIPT_INSTALL_PATH ${LLDB_FRAMEWORK_INSTALL_DIR}/LLDB.framework/Resources/JavaScript)
+ else()
+ set(LLDB_JAVASCRIPT_INSTALL_PATH lib/javascript)
+ endif()
+ install(DIRECTORY ${lldb_javascript_target_dir}/
+ DESTINATION ${LLDB_JAVASCRIPT_INSTALL_PATH}
+ COMPONENT ${lldb_javascript_library_target})
+
+ set(lldb_javascript_library_install_target "install-${lldb_javascript_library_target}")
+ if (NOT LLVM_ENABLE_IDE)
+ add_llvm_install_targets(${lldb_javascript_library_install_target}
+ COMPONENT ${lldb_javascript_library_target}
+ DEPENDS ${lldb_javascript_library_target})
+ endif()
+endfunction()
diff --git a/lldb/bindings/javascript/javascript-swigsafecast.swig b/lldb/bindings/javascript/javascript-swigsafecast.swig
new file mode 100644
index 0000000000000..5d2b50f1fd249
--- /dev/null
+++ b/lldb/bindings/javascript/javascript-swigsafecast.swig
@@ -0,0 +1,8 @@
+/*
+ Safe casting for JavaScript SWIG bindings
+*/
+
+// This file provides safe type casting between LLDB types
+// Similar to lua-swigsafecast.swig and python-swigsafecast.swig
+
+// TODO: Implement safe casting functions as needed
diff --git a/lldb/bindings/javascript/javascript-typemaps.swig b/lldb/bindings/javascript/javascript-typemaps.swig
new file mode 100644
index 0000000000000..cc11067e712bd
--- /dev/null
+++ b/lldb/bindings/javascript/javascript-typemaps.swig
@@ -0,0 +1,48 @@
+/*
+ JavaScript-specific typemaps for LLDB
+*/
+
+%header %{
+#include <v8.h>
+%}
+
+// Typemap for char ** (string arrays) - used in LaunchSimple, Launch, etc.
+// Converts JavaScript arrays to C string arrays
+%typemap(in) char ** {
+ if ($input->IsArray()) {
+ v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast($input);
+ uint32_t length = array->Length();
+ $1 = (char **)malloc((length + 1) * sizeof(char *));
+
+ for (uint32_t i = 0; i < length; i++) {
+ v8::Local<v8::Value> element;
+ if (array->Get(SWIGV8_CURRENT_CONTEXT(), i).ToLocal(&element)) {
+ if (element->IsString()) {
+ v8::String::Utf8Value str(SWIGV8_CURRENT_CONTEXT()->GetIsolate(), element);
+ $1[i] = strdup(*str);
+ } else {
+ free($1);
+ SWIG_exception_fail(SWIG_TypeError, "Array elements must be strings");
+ }
+ }
+ }
+ $1[length] = NULL;
+ } else if ($input->IsNull() || $input->IsUndefined()) {
+ $1 = NULL;
+ } else {
+ SWIG_exception_fail(SWIG_TypeError, "Expected array of strings or null");
+ }
+}
+
+%typemap(freearg) char ** {
+ if ($1) {
+ for (int i = 0; $1[i] != NULL; i++) {
+ free($1[i]);
+ }
+ free($1);
+ }
+}
+
+%typemap(typecheck, precedence=SWIG_TYPECHECK_STRING_ARRAY) char ** {
+ $1 = $input->IsArray() || $input->IsNull() || $input->IsUndefined();
+}
diff --git a/lldb/bindings/javascript/javascript-wrapper.swig b/lldb/bindings/javascript/javascript-wrapper.swig
new file mode 100644
index 0000000000000..af952201ddd05
--- /dev/null
+++ b/lldb/bindings/javascript/javascript-wrapper.swig
@@ -0,0 +1,12 @@
+/*
+ JavaScript-specific wrapper functions for LLDB
+*/
+
+// This file will contain JavaScript-specific wrapper code
+// to bridge between LLDB's C++ API and JavaScript/V8
+
+// TODO: Add wrapper functions for:
+// - Breakpoint callbacks
+// - Watchpoint callbacks
+// - Custom commands
+// - Data formatters
diff --git a/lldb/bindings/javascript/javascript.swig b/lldb/bindings/javascript/javascript.swig
new file mode 100644
index 0000000000000..098c04ad8d365
--- /dev/null
+++ b/lldb/bindings/javascript/javascript.swig
@@ -0,0 +1,30 @@
+/*
+ lldb.swig
+
+ This is the input file for SWIG, to create the appropriate C++ wrappers and
+ functions for JavaScript (V8/Node.js), to enable them to call the
+ liblldb Script Bridge functions.
+*/
+
+%module lldb
+
+%include <std_string.i>
+%include "javascript-typemaps.swig"
+%include "macros.swig"
+%include "headers.swig"
+
+%{
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "../bindings/javascript/javascript-swigsafecast.swig"
+#include "../source/Plugins/ScriptInterpreter/JavaScript/SWIGJavaScriptBridge.h"
+
+// required headers for typemaps
+#include "lldb/Host/File.h"
+
+using namespace lldb_private;
+using namespace lldb;
+%}
+
+%include "interfaces.swig"
+%include "javascript-wrapper.swig"
diff --git a/lldb/cmake/modules/FindV8.cmake b/lldb/cmake/modules/FindV8.cmake
new file mode 100644
index 0000000000000..d6ce23feddef1
--- /dev/null
+++ b/lldb/cmake/modules/FindV8.cmake
@@ -0,0 +1,71 @@
+#.rst:
+# FindV8
+# ------
+#
+# Find V8 JavaScript engine
+#
+# This module will search for V8 in standard system locations, or use
+# user-specified paths. Users can override the search by setting:
+# -DV8_INCLUDE_DIR=/path/to/v8/include
+# -DV8_LIBRARIES=/path/to/libv8.so (or libv8_monolith.a)
+#
+# The module defines:
+# V8_FOUND - System has V8
+# V8_INCLUDE_DIR - V8 include directory
+# V8_LIBRARIES - V8 libraries to link against
+
+if(V8_LIBRARIES AND V8_INCLUDE_DIR)
+ set(V8_FOUND TRUE)
+ if(NOT V8_FIND_QUIETLY)
+ message(STATUS "Found V8: ${V8_INCLUDE_DIR}")
+ message(STATUS "Found V8 library: ${V8_LIBRARIES}")
+ set(V8_FIND_QUIETLY TRUE CACHE BOOL "Suppress repeated V8 find messages" FORCE)
+ endif()
+else()
+ # Try to find system V8
+ find_path(V8_INCLUDE_DIR
+ NAMES v8.h
+ PATHS
+ # Standard system locations
+ /usr/include
+ /usr/local/include
+ /opt/v8/include
+ # Homebrew on macOS
+ /opt/homebrew/include
+ /usr/local/opt/v8/include
+ PATH_SUFFIXES
+ v8
+ DOC "V8 include directory"
+ )
+
+ find_library(V8_LIBRARIES
+ NAMES v8_monolith v8 v8_libbase v8_libplatform
+ PATHS
+ # Standard system locations
+ /usr/lib
+ /usr/local/lib
+ /opt/v8/lib
+ # Homebrew on macOS
+ /opt/homebrew/lib
+ /usr/local/opt/v8/lib
+ DOC "V8 library"
+ )
+
+ include(FindPackageHandleStandardArgs)
+ find_package_handle_standard_args(V8
+ FOUND_VAR
+ V8_FOUND
+ REQUIRED_VARS
+ V8_INCLUDE_DIR
+ V8_LIBRARIES)
+
+ if(V8_FOUND)
+ mark_as_advanced(V8_LIBRARIES V8_INCLUDE_DIR)
+ message(STATUS "Found V8: ${V8_INCLUDE_DIR}")
+ if(V8_LIBRARIES)
+ message(STATUS "Found V8 library: ${V8_LIBRARIES}")
+ else()
+ message(STATUS "V8 headers found (library may need to be built or specified manually)")
+ endif()
+ endif()
+endif()
diff --git a/lldb/cmake/modules/LLDBConfig.cmake b/lldb/cmake/modules/LLDBConfig.cmake
index 4b568d27c4709..e42522e8b8765 100644
--- a/lldb/cmake/modules/LLDBConfig.cmake
+++ b/lldb/cmake/modules/LLDBConfig.cmake
@@ -62,6 +62,7 @@ add_optional_dependency(LLDB_ENABLE_CURSES "Enable curses support in LLDB" Curse
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_PYTHON "Enable Python scripting support in LLDB" PythonAndSwig PYTHONANDSWIG_FOUND)
+add_optional_dependency(LLDB_ENABLE_JAVASCRIPT "Enable JavaScript scripting support in LLDB" V8 V8_FOUND)
add_optional_dependency(LLDB_ENABLE_LIBXML2 "Enable Libxml 2 support in LLDB" LibXml2 LIBXML2_FOUND VERSION ${LLDB_LIBXML2_VERSION})
add_optional_dependency(LLDB_ENABLE_FBSDVMCORE "Enable libfbsdvmcore support in LLDB" FBSDVMCore FBSDVMCore_FOUND QUIET)
diff --git a/lldb/docs/index.rst b/lldb/docs/index.rst
index a981c0ab8d6e9..bfe39dc9d5297 100644
--- a/lldb/docs/index.rst
+++ b/lldb/docs/index.rst
@@ -27,7 +27,9 @@ with GDB there is a cheat sheet listing common tasks and their LLDB equivalent
in the `GDB to LLDB command map <https://lldb.llvm.org/use/map.html>`_.
There are also multiple resources on how to script LLDB using Python: the
-:doc:`use/python-reference` is a great starting point for that.
+:doc:`use/python-reference` is a great starting point for that. LLDB also
+supports scripting with JavaScript through the V8 engine (see
+`JavaScript Reference <use/javascript-reference.html>`_).
Compiler Integration Benefits
-----------------------------
@@ -148,6 +150,7 @@ interesting areas to contribute to lldb.
use/python
use/python-reference
+ use/javascript-reference
Python API <python_api>
Python Extensions <python_extensions>
diff --git a/lldb/docs/resources/build.rst b/lldb/docs/resources/build.rst
index 0db8c92ad49d6..5d6ee39ea6164 100644
--- a/lldb/docs/resources/build.rst
+++ b/lldb/docs/resources/build.rst
@@ -66,6 +66,8 @@ CMake configuration error.
+-------------------+--------------------------------------------------------------+--------------------------+
| Lua | Lua scripting. Lua 5.3 and 5.4 are supported. | ``LLDB_ENABLE_LUA`` |
+-------------------+--------------------------------------------------------------+--------------------------+
+| JavaScript | JavaScript scripting via V8 engine. Experimental. | ``LLDB_ENABLE_JAVASCRIPT``|
++-------------------+--------------------------------------------------------------+--------------------------+
Depending on your platform and package manager, one might run any of the
commands below.
diff --git a/lldb/docs/use/javascript-reference.md b/lldb/docs/use/javascript-reference.md
new file mode 100644
index 0000000000000..a73befa02f224
--- /dev/null
+++ b/lldb/docs/use/javascript-reference.md
@@ -0,0 +1,263 @@
+# JavaScript Reference
+
+LLDB has extensive support for interacting with JavaScript through the V8
+JavaScript engine. This document describes how to use JavaScript scripting
+within LLDB and provides reference documentation for the JavaScript API.
+
+## Using JavaScript in LLDB
+
+LLDB's JavaScript support is built on top of the V8 JavaScript engine, the same
+engine that powers Node.js and Chrome. This provides full ES2020+ language
+support with modern JavaScript features.
+
+### Interactive JavaScript
+
+JavaScript can be run interactively in LLDB. First, set JavaScript as the script language, then use the `script` command:
+
+```
+(lldb) settings set script-lang javascript
+(lldb) script
+>>> let message = "Hello from JavaScript!";
+>>> console.log(message);
+Hello from JavaScript!
+>>> lldb.debugger.GetVersionString()
+lldb version 18.0.0
+```
+
+### Running JavaScript from Files
+
+You can execute JavaScript files using the `command script import` command:
+
+```
+(lldb) command script import /path/to/myscript.js
+```
+
+The JavaScript file will be executed in the current LLDB context with access
+to all LLDB APIs.
+
+### Example JavaScript Script
+
+Here's a simple example that demonstrates using the LLDB JavaScript API:
+
+```javascript
+// Get the current debugger instance
+let debugger = lldb.debugger;
+
+// Get the current target
+let target = debugger.GetSelectedTarget();
+
+// Get the current process
+let process = target.GetProcess();
+
+// Get the selected thread
+let thread = process.GetSelectedThread();
+
+// Get the selected frame
+let frame = thread.GetSelectedFrame();
+
+// Evaluate an expression
+let result = frame.EvaluateExpression("myVariable");
+console.log("Value:", result.GetValue());
+
+// Print all local variables
+let variables = frame.GetVariables(true, true, false, false);
+for (let i = 0; i < variables.GetSize(); i++) {
+ let variable = variables.GetValueAtIndex(i);
+ console.log(variable.GetName() + " = " + variable.GetValue());
+}
+```
+
+## The JavaScript API
+
+The JavaScript API provides access to all of LLDB's Script Bridge (SB) API
+classes. These classes are automatically available in the `lldb` module when
+running JavaScript within LLDB.
+
+### Global Objects
+
+* `lldb`: The main LLDB module containing all SB API classes
+* `lldb.debugger`: The current debugger instance (shortcut to avoid passing
+ debugger around)
+* `console`: Standard JavaScript console object for logging
+
+### Available Classes
+
+The JavaScript API includes all of LLDB's SB API classes, like `SBDebugger`,
+`SBTarget`, etc.
+
+For complete documentation of all classes and their methods, refer to the
+[C++ API documentation](https://lldb.llvm.org/cpp_reference/namespacelldb.html),
+as the JavaScript API mirrors the C++ API closely.
+
+### Console Output
+
+JavaScript scripts can use the standard `console` object for output:
+
+```javascript
+console.log("Informational message");
+console.error("Error message");
+console.warn("Warning message");
+```
+
+Output from `console.log()` and other console methods will be displayed in
+the LLDB command output.
+
+## Building LLDB with JavaScript Support
+
+### Prerequisites
+
+To build LLDB with JavaScript support, you need:
+
+* [V8 JavaScript Engine](https://v8.dev) (version 8.0 or later recommended)
+* [SWIG](http://swig.org/) 4 or later (for generating language bindings)
+* All standard LLDB build dependencies (see [build documentation](../resources/build.rst))
+
+### Installing V8
+
+The V8 JavaScript engine must be installed on your system. Installation methods
+vary by platform:
+
+**Ubuntu/Debian:**
+
+```bash
+$ sudo apt-get install libv8-dev
+```
+
+After installation, V8 will typically be installed in:
+- Headers: `/usr/include/v8/` or `/usr/include/`
+- Libraries: `/usr/lib/x86_64-linux-gnu/libv8.so` (or similar for your architecture)
+
+You can verify the installation with:
+```bash
+$ dpkg -L libv8-dev | grep -E '(include|lib)'
+```
+
+**macOS (using Homebrew):**
+
+```bash
+$ brew install v8
+```
+
+After installation, you can find the paths with:
+```bash
+$ brew info v8
+```
+
+Homebrew typically installs to `/opt/homebrew/` (Apple Silicon) or `/usr/local/` (Intel).
+
+**Building V8 from source:**
+
+If V8 is not available as a package for your platform, you can build it from
+source. Follow the instructions at https://v8.dev/docs/build
+
+### CMake Configuration
+
+To enable JavaScript support when building LLDB, add the following CMake
+options:
+
+```bash
+$ cmake -G Ninja \
+ -DLLDB_ENABLE_JAVASCRIPT=ON \
+ [other cmake options] \
+ /path/to/llvm-project/llvm
+```
+
+The `LLDB_ENABLE_JAVASCRIPT` flag enables JavaScript scripting support. If
+V8 is installed via a package manager in standard system locations, CMake
+should auto-detect it. If CMake cannot find V8, you can specify the paths
+manually:
+
+```bash
+$ cmake -G Ninja \
+ -DLLDB_ENABLE_JAVASCRIPT=ON \
+ -DV8_INCLUDE_DIR=/path/to/v8/include \
+ -DV8_LIBRARIES=/path/to/v8/lib/libv8.so \
+ [other cmake options] \
+ /path/to/llvm-project/llvm
+```
+
+where:
+* `V8_INCLUDE_DIR`: Path to V8 header files
+* `V8_LIBRARIES`: Path to V8 library files
+
+### Verifying JavaScript Support
+
+After building LLDB with JavaScript support, you can verify it's working:
+
+```
+$ lldb
+(lldb) settings set script-lang javascript
+(lldb) script
+>>> console.log("JavaScript is working!")
+JavaScript is working!
+>>> lldb.debugger.GetVersionString()
+lldb version 18.0.0
+```
+
+If JavaScript support is not enabled, you'll see an error message when trying
+to set the script language to JavaScript.
+
+### Build Example
+
+Here's a complete example of building LLDB with JavaScript support from scratch:
+
+```bash
+# Clone the LLVM project
+$ git clone https://github.com/llvm/llvm-project.git
+
+# Create build directory
+$ mkdir llvm-build && cd llvm-build
+
+# Configure with JavaScript support
+$ cmake -G Ninja \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DLLVM_ENABLE_PROJECTS="clang;lldb" \
+ -DLLDB_ENABLE_JAVASCRIPT=ON \
+ -DV8_INCLUDE_DIR=/usr/include/v8 \
+ -DV8_LIBRARIES=/usr/lib/x86_64-linux-gnu/libv8.so \
+ ../llvm-project/llvm
+
+# Build LLDB
+$ ninja lldb
+
+# Test JavaScript support
+$ ./bin/lldb -o "settings set script-lang javascript" -o "script -e \"console.log('Hello!')\"" -o "quit"
+```
+
+## Differences from Python API and JavaScript Environment
+
+Important differences to understand:
+
+**Not a Node.js Environment:**
+
+LLDB's JavaScript environment uses the V8 engine but is **not** Node.js. This means:
+
+* **No module system**: `import`, `require()`, and `module.exports` are not available
+* **No event loop**: Asynchronous operations like `setTimeout`, `setInterval`, `Promise.then()` callbacks are not supported
+* **Limited global APIs**: Only specific functions are implemented:
+ * `console.log()`, `console.error()`, `console.warn()` for output
+ * `lldb` global object for LLDB API access
+ * Standard JavaScript language features (ES2020+)
+
+**Module Access:**
+
+In Python, you typically import with `import lldb`. In JavaScript, `lldb`
+is automatically available as a global object without any import statement.
+
+Scripts should be written as self-contained synchronous code that directly uses the
+`lldb` global object.
+
+## Known Limitations
+
+The JavaScript support in LLDB is not as extensive as Python. The
+following features are not yet implemented:
+
+* Custom breakpoint callbacks in JavaScript
+* Custom watchpoint callbacks in JavaScript
+* Some advanced type mapping and conversions
+
+## Additional Resources
+
+* [LLDB C++ API Reference](https://lldb.llvm.org/cpp_reference/namespacelldb.html)
+* [V8 JavaScript Engine Documentation](https://v8.dev/docs)
+* [LLDB Python Reference](python-reference.html) (similar concepts apply to JavaScript)
diff --git a/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h b/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h
index 4face717531b1..9e4ab5d1425e6 100644
--- a/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h
+++ b/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h
@@ -96,6 +96,11 @@ static constexpr OptionEnumValueElement g_script_option_enumeration[] = {
"lua",
"Commands are in the Lua language.",
},
+ {
+ lldb::eScriptLanguageJavaScript,
+ "javascript",
+ "Commands are in the JavaScript language.",
+ },
{
lldb::eScriptLanguageNone,
"default",
diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h
index 1a7db8faecd94..879c25e3ae0d5 100644
--- a/lldb/include/lldb/lldb-enumerations.h
+++ b/lldb/include/lldb/lldb-enumerations.h
@@ -227,8 +227,17 @@ enum ScriptLanguage {
eScriptLanguageNone = 0,
eScriptLanguagePython,
eScriptLanguageLua,
+ eScriptLanguageJavaScript,
eScriptLanguageUnknown,
+#if LLDB_ENABLE_PYTHON
eScriptLanguageDefault = eScriptLanguagePython
+#elif LLDB_ENABLE_LUA
+ eScriptLanguageDefault = eScriptLanguageLua
+#elif LLDB_ENABLE_JAVASCRIPT
+ eScriptLanguageDefault = eScriptLanguageJavaScript
+#else
+ eScriptLanguageDefault = eScriptLanguageNone
+#endif
};
/// Register numbering types.
@@ -314,7 +323,7 @@ enum ConnectionStatus {
eConnectionStatusNoConnection, ///< No connection
eConnectionStatusLostConnection, ///< Lost connection while connected to a
///< valid connection
- eConnectionStatusInterrupted ///< Interrupted read
+ eConnectionStatusInterrupted ///< Interrupted read
};
enum ErrorType {
@@ -1109,7 +1118,7 @@ enum PathType {
ePathTypeGlobalLLDBTempSystemDir, ///< The LLDB temp directory for this
///< system, NOT cleaned up on a process
///< exit.
- ePathTypeClangDir ///< Find path to Clang builtin headers
+ ePathTypeClangDir ///< Find path to Clang builtin headers
};
/// Kind of member function.
diff --git a/lldb/source/Commands/CommandObjectBreakpointCommand.cpp b/lldb/source/Commands/CommandObjectBreakpointCommand.cpp
index a913ed5fa12b3..8e60ebce987bd 100644
--- a/lldb/source/Commands/CommandObjectBreakpointCommand.cpp
+++ b/lldb/source/Commands/CommandObjectBreakpointCommand.cpp
@@ -266,6 +266,7 @@ are no syntax errors may indicate that a function was declared but never called.
switch (m_script_language) {
case eScriptLanguagePython:
case eScriptLanguageLua:
+ case eScriptLanguageJavaScript:
m_use_script_language = true;
break;
case eScriptLanguageNone:
diff --git a/lldb/source/Commands/CommandObjectWatchpointCommand.cpp b/lldb/source/Commands/CommandObjectWatchpointCommand.cpp
index 062bf75eb8ae8..1e5543096e5a2 100644
--- a/lldb/source/Commands/CommandObjectWatchpointCommand.cpp
+++ b/lldb/source/Commands/CommandObjectWatchpointCommand.cpp
@@ -297,6 +297,7 @@ are no syntax errors may indicate that a function was declared but never called.
switch (m_script_language) {
case eScriptLanguagePython:
case eScriptLanguageLua:
+ case eScriptLanguageJavaScript:
m_use_script_language = true;
break;
case eScriptLanguageNone:
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index b37d9d3ed85e3..d8a6fd44c7f3f 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -147,6 +147,11 @@ static constexpr OptionEnumValueElement g_language_enumerators[] = {
"python",
"Select python as the default scripting language.",
},
+ {
+ eScriptLanguageJavaScript,
+ "javascript",
+ "Select javascript as the default scripting language.",
+ },
{
eScriptLanguageDefault,
"default",
diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp
index ca768db1199c1..b215710a545f0 100644
--- a/lldb/source/Interpreter/ScriptInterpreter.cpp
+++ b/lldb/source/Interpreter/ScriptInterpreter.cpp
@@ -65,6 +65,8 @@ std::string ScriptInterpreter::LanguageToString(lldb::ScriptLanguage language) {
return "Python";
case eScriptLanguageLua:
return "Lua";
+ case eScriptLanguageJavaScript:
+ return "JavaScript";
case eScriptLanguageUnknown:
return "Unknown";
}
@@ -158,6 +160,8 @@ ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) {
return eScriptLanguagePython;
if (language.equals_insensitive(LanguageToString(eScriptLanguageLua)))
return eScriptLanguageLua;
+ if (language.equals_insensitive(LanguageToString(eScriptLanguageJavaScript)))
+ return eScriptLanguageJavaScript;
return eScriptLanguageUnknown;
}
diff --git a/lldb/source/Plugins/ScriptInterpreter/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/CMakeLists.txt
index 4429b006173a7..ae74db4db31c0 100644
--- a/lldb/source/Plugins/ScriptInterpreter/CMakeLists.txt
+++ b/lldb/source/Plugins/ScriptInterpreter/CMakeLists.txt
@@ -8,3 +8,7 @@ endif()
if (LLDB_ENABLE_LUA)
add_subdirectory(Lua)
endif()
+
+if (LLDB_ENABLE_JAVASCRIPT)
+ add_subdirectory(JavaScript)
+endif()
diff --git a/lldb/source/Plugins/ScriptInterpreter/JavaScript/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/JavaScript/CMakeLists.txt
new file mode 100644
index 0000000000000..e7c5baffcaed5
--- /dev/null
+++ b/lldb/source/Plugins/ScriptInterpreter/JavaScript/CMakeLists.txt
@@ -0,0 +1,40 @@
+if(NOT LLDB_ENABLE_JAVASCRIPT)
+ return()
+endif()
+
+find_package(V8)
+
+if(NOT V8_FOUND)
+ message(FATAL_ERROR "V8 JavaScript engine not found. JavaScript scripting will not be available.")
+ return()
+endif()
+
+add_lldb_library(lldbPluginScriptInterpreterJavaScript PLUGIN
+ JavaScript.cpp
+ ScriptInterpreterJavaScript.cpp
+ ${CMAKE_BINARY_DIR}/tools/lldb/bindings/javascript/LLDBWrapJavaScript.cpp
+
+ LINK_LIBS
+ lldbBreakpoint
+ lldbCore
+ lldbDataFormatters
+ lldbHost
+ lldbInterpreter
+ lldbTarget
+ lldbUtility
+
+ LINK_COMPONENTS
+ Support
+
+ CLANG_LIBS
+ clangBasic
+)
+
+target_include_directories(lldbPluginScriptInterpreterJavaScript PUBLIC
+ ${V8_INCLUDE_DIR}
+)
+
+# Link against V8
+if(V8_LIBRARIES)
+ target_link_libraries(lldbPluginScriptInterpreterJavaScript PRIVATE ${V8_LIBRARIES})
+endif()
diff --git a/lldb/source/Plugins/ScriptInterpreter/JavaScript/JavaScript.cpp b/lldb/source/Plugins/ScriptInterpreter/JavaScript/JavaScript.cpp
new file mode 100644
index 0000000000000..a5c4074dabc3b
--- /dev/null
+++ b/lldb/source/Plugins/ScriptInterpreter/JavaScript/JavaScript.cpp
@@ -0,0 +1,435 @@
+//===-- JavaScript.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 "JavaScript.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Utility/FileSpec.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FormatVariadic.h"
+
+#include <libplatform/libplatform.h>
+#include <v8.h>
+
+using namespace lldb_private;
+using namespace lldb;
+
+// SWIG-generated init function (SWIGV8_INIT is a macro that expands to
+// lldb_initialize)
+extern "C" void lldb_initialize(v8::Local<v8::Object> exports,
+ v8::Local<v8::Object> module);
+
+// Static V8 platform (initialized once)
+std::unique_ptr<v8::Platform> JavaScript::s_platform;
+bool JavaScript::s_platform_initialized = false;
+
+// Helper to format and write output
+static void
+WriteFormattedOutput(const v8::FunctionCallbackInfo<v8::Value> &args,
+ bool add_newline = true) {
+ v8::Isolate *isolate = args.GetIsolate();
+ v8::HandleScope handle_scope(isolate);
+
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
+ JavaScript *js_instance =
+ static_cast<JavaScript *>(context->GetAlignedPointerFromEmbedderData(1));
+
+ std::string output;
+ for (int i = 0; i < args.Length(); i++) {
+ if (i > 0)
+ output += " ";
+ v8::String::Utf8Value str(isolate, args[i]);
+ output += *str;
+ }
+ if (add_newline)
+ output += "\n";
+
+ if (js_instance) {
+ js_instance->WriteOutput(output);
+ } else {
+ printf("%s", output.c_str());
+ fflush(stdout);
+ }
+}
+
+// Console.log implementation
+static void ConsoleLog(const v8::FunctionCallbackInfo<v8::Value> &args) {
+ WriteFormattedOutput(args, true);
+}
+
+// Console.warn implementation
+static void ConsoleWarn(const v8::FunctionCallbackInfo<v8::Value> &args) {
+ WriteFormattedOutput(args, true);
+}
+
+// Console.error implementation
+static void ConsoleError(const v8::FunctionCallbackInfo<v8::Value> &args) {
+ WriteFormattedOutput(args, true);
+}
+
+void JavaScript::InitializePlatform() {
+ if (s_platform_initialized)
+ return;
+
+ v8::V8::InitializeICUDefaultLocation("");
+ v8::V8::InitializeExternalStartupData("");
+ s_platform = v8::platform::NewDefaultPlatform();
+ v8::V8::InitializePlatform(s_platform.get());
+ v8::V8::Initialize();
+
+ s_platform_initialized = true;
+}
+
+JavaScript::JavaScript(lldb::FileSP output_file)
+ : m_stdout(stdout), m_stderr(stderr), m_output_file(output_file) {
+ InitializePlatform();
+
+ // Create isolate
+ v8::Isolate::CreateParams create_params;
+ create_params.array_buffer_allocator =
+ v8::ArrayBuffer::Allocator::NewDefaultAllocator();
+ m_isolate = v8::Isolate::New(create_params);
+
+ // Create context
+ v8::Isolate::Scope isolate_scope(m_isolate);
+ v8::HandleScope handle_scope(m_isolate);
+
+ v8::Local<v8::Context> context = v8::Context::New(m_isolate);
+ m_context = new v8::Global<v8::Context>(m_isolate, context);
+
+ // Initialize SWIG bindings
+ v8::Context::Scope context_scope(context);
+ v8::Local<v8::Object> lldb_module = v8::Object::New(m_isolate);
+ v8::Local<v8::Object> empty_module = v8::Object::New(m_isolate);
+
+ lldb_initialize(lldb_module, empty_module);
+
+ context->Global()
+ ->Set(context,
+ v8::String::NewFromUtf8(m_isolate, "lldb").ToLocalChecked(),
+ lldb_module)
+ .Check();
+
+ v8::Local<v8::Object> console_obj = v8::Object::New(m_isolate);
+ console_obj
+ ->Set(context, v8::String::NewFromUtf8(m_isolate, "log").ToLocalChecked(),
+ v8::Function::New(context, ConsoleLog).ToLocalChecked())
+ .Check();
+ console_obj
+ ->Set(context,
+ v8::String::NewFromUtf8(m_isolate, "warn").ToLocalChecked(),
+ v8::Function::New(context, ConsoleWarn).ToLocalChecked())
+ .Check();
+ console_obj
+ ->Set(context,
+ v8::String::NewFromUtf8(m_isolate, "error").ToLocalChecked(),
+ v8::Function::New(context, ConsoleError).ToLocalChecked())
+ .Check();
+ context->Global()
+ ->Set(context,
+ v8::String::NewFromUtf8(m_isolate, "console").ToLocalChecked(),
+ console_obj)
+ .Check();
+
+ context->SetAlignedPointerInEmbedderData(1, this);
+}
+
+JavaScript::~JavaScript() {
+ // Clear all callbacks
+ for (auto &pair : m_breakpoint_callbacks) {
+ pair.second.Reset();
+ }
+ m_breakpoint_callbacks.clear();
+
+ for (auto &pair : m_watchpoint_callbacks) {
+ pair.second.Reset();
+ }
+ m_watchpoint_callbacks.clear();
+
+ if (m_context) {
+ m_context->Reset();
+ delete m_context;
+ }
+ if (m_isolate) {
+ m_isolate->Dispose();
+ }
+}
+
+llvm::Error JavaScript::Run(llvm::StringRef code) {
+ v8::Isolate::Scope isolate_scope(m_isolate);
+ v8::HandleScope handle_scope(m_isolate);
+ v8::Local<v8::Context> context = m_context->Get(m_isolate);
+ v8::Context::Scope context_scope(context);
+
+ v8::TryCatch try_catch(m_isolate);
+
+ // Compile
+ v8::Local<v8::String> source =
+ v8::String::NewFromUtf8(m_isolate, code.data(),
+ v8::NewStringType::kNormal, code.size())
+ .ToLocalChecked();
+
+ v8::Local<v8::Script> script;
+ if (!v8::Script::Compile(context, source).ToLocal(&script)) {
+ v8::String::Utf8Value error(m_isolate, try_catch.Exception());
+ return llvm::make_error<llvm::StringError>(
+ llvm::formatv("Compilation error: {0}\n", *error),
+ llvm::inconvertibleErrorCode());
+ }
+
+ // Run
+ v8::Local<v8::Value> result;
+ if (!script->Run(context).ToLocal(&result)) {
+ v8::String::Utf8Value error(m_isolate, try_catch.Exception());
+ return llvm::make_error<llvm::StringError>(
+ llvm::formatv("Runtime error: {0}\n", *error),
+ llvm::inconvertibleErrorCode());
+ }
+
+ // Print the result if it's not undefined (REPL behavior)
+ if (!result->IsUndefined()) {
+ v8::String::Utf8Value result_str(m_isolate, result);
+ WriteOutput(std::string(*result_str) + "\n");
+ }
+
+ return llvm::Error::success();
+}
+
+llvm::Error JavaScript::LoadModule(llvm::StringRef filename) {
+ const FileSpec file(filename);
+ if (!FileSystem::Instance().Exists(file)) {
+ return llvm::make_error<llvm::StringError>("File not found",
+ llvm::inconvertibleErrorCode());
+ }
+
+ if (file.GetFileNameExtension() != ".js") {
+ return llvm::make_error<llvm::StringError>(
+ "Invalid extension (expected .js)", llvm::inconvertibleErrorCode());
+ }
+
+ // Read file using llvm MemoryBuffer
+ auto buffer_or_error = llvm::MemoryBuffer::getFile(file.GetPath());
+ if (!buffer_or_error) {
+ return llvm::make_error<llvm::StringError>(
+ llvm::formatv("Failed to read file: {0}",
+ buffer_or_error.getError().message()),
+ llvm::inconvertibleErrorCode());
+ }
+
+ std::unique_ptr<llvm::MemoryBuffer> buffer = std::move(*buffer_or_error);
+ llvm::StringRef contents = buffer->getBuffer();
+
+ if (contents.empty()) {
+ return llvm::make_error<llvm::StringError>("Empty file",
+ llvm::inconvertibleErrorCode());
+ }
+
+ return Run(contents);
+}
+
+llvm::Error JavaScript::CheckSyntax(llvm::StringRef code) {
+ v8::Isolate::Scope isolate_scope(m_isolate);
+ v8::HandleScope handle_scope(m_isolate);
+ v8::Local<v8::Context> context = m_context->Get(m_isolate);
+ v8::Context::Scope context_scope(context);
+
+ v8::TryCatch try_catch(m_isolate);
+
+ v8::Local<v8::String> source =
+ v8::String::NewFromUtf8(m_isolate, code.data(),
+ v8::NewStringType::kNormal, code.size())
+ .ToLocalChecked();
+
+ v8::Local<v8::Script> script;
+ if (!v8::Script::Compile(context, source).ToLocal(&script)) {
+ v8::String::Utf8Value error(m_isolate, try_catch.Exception());
+ return llvm::make_error<llvm::StringError>(
+ llvm::formatv("Syntax error: {0}\n", *error),
+ llvm::inconvertibleErrorCode());
+ }
+
+ return llvm::Error::success();
+}
+
+llvm::Error JavaScript::ChangeIO(FILE *out, FILE *err) {
+ m_stdout = out;
+ m_stderr = err;
+ return llvm::Error::success();
+}
+
+llvm::Error
+JavaScript::RegisterBreakpointCallback(void *baton,
+ const char *command_body_text) {
+ v8::Isolate::Scope isolate_scope(m_isolate);
+ v8::HandleScope handle_scope(m_isolate);
+ v8::Local<v8::Context> context = m_context->Get(m_isolate);
+ v8::Context::Scope context_scope(context);
+
+ v8::TryCatch try_catch(m_isolate);
+
+ v8::Local<v8::String> source =
+ v8::String::NewFromUtf8(m_isolate, command_body_text,
+ v8::NewStringType::kNormal,
+ strlen(command_body_text))
+ .ToLocalChecked();
+
+ v8::Local<v8::Script> script;
+ if (!v8::Script::Compile(context, source).ToLocal(&script)) {
+ v8::String::Utf8Value error(m_isolate, try_catch.Exception());
+ return llvm::make_error<llvm::StringError>(
+ llvm::formatv("Failed to compile callback: {0}", *error),
+ llvm::inconvertibleErrorCode());
+ }
+
+ v8::Local<v8::Value> result;
+ if (!script->Run(context).ToLocal(&result)) {
+ v8::String::Utf8Value error(m_isolate, try_catch.Exception());
+ return llvm::make_error<llvm::StringError>(
+ llvm::formatv("Failed to evaluate callback: {0}", *error),
+ llvm::inconvertibleErrorCode());
+ }
+
+ if (!result->IsFunction()) {
+ return llvm::make_error<llvm::StringError>(
+ "Breakpoint callback must be a JavaScript function",
+ llvm::inconvertibleErrorCode());
+ }
+
+ // Store the function in our map
+ v8::Local<v8::Function> callback = result.As<v8::Function>();
+ m_breakpoint_callbacks[baton] = v8::Global<v8::Function>(m_isolate, callback);
+
+ return llvm::Error::success();
+}
+
+llvm::Expected<bool>
+JavaScript::CallBreakpointCallback(void *baton,
+ lldb::StackFrameSP stop_frame_sp,
+ lldb::BreakpointLocationSP bp_loc_sp,
+ StructuredData::ObjectSP extra_args_sp) {
+ auto it = m_breakpoint_callbacks.find(baton);
+ if (it == m_breakpoint_callbacks.end()) {
+ return llvm::make_error<llvm::StringError>(
+ "No callback registered for this baton",
+ llvm::inconvertibleErrorCode());
+ }
+
+ v8::Isolate::Scope isolate_scope(m_isolate);
+ v8::HandleScope handle_scope(m_isolate);
+ v8::Local<v8::Context> context = m_context->Get(m_isolate);
+ v8::Context::Scope context_scope(context);
+
+ v8::TryCatch try_catch(m_isolate);
+
+ v8::Local<v8::Function> callback = it->second.Get(m_isolate);
+
+ v8::Local<v8::Value> args[2] = {v8::Null(m_isolate), v8::Null(m_isolate)};
+
+ v8::Local<v8::Value> result;
+ if (!callback->Call(context, context->Global(), 2, args).ToLocal(&result)) {
+ v8::String::Utf8Value error(m_isolate, try_catch.Exception());
+ WriteOutput(
+ llvm::formatv("Breakpoint callback error: {0}\n", *error).str());
+ return false;
+ }
+
+ bool should_stop = false;
+ if (result->IsBoolean()) {
+ should_stop = result->BooleanValue(m_isolate);
+ }
+
+ return should_stop;
+}
+
+llvm::Error
+JavaScript::RegisterWatchpointCallback(void *baton,
+ const char *command_body_text) {
+ return llvm::make_error<llvm::StringError>(
+ "Watchpoint callbacks not yet implemented",
+ llvm::inconvertibleErrorCode());
+}
+
+llvm::Expected<bool> JavaScript::CallWatchpointCallback(
+ void *baton, lldb::StackFrameSP stop_frame_sp, lldb::WatchpointSP wp_sp) {
+ return false;
+}
+
+void JavaScript::SetOutputCallback(OutputCallback callback) {
+ m_output_callback = callback;
+}
+
+void JavaScript::WriteOutput(const std::string &text) {
+ if (m_output_callback) {
+ m_output_callback(text);
+ } else if (m_output_file && m_output_file->IsValid()) {
+ m_output_file->Printf("%s", text.c_str());
+ m_output_file->Flush();
+ } else {
+ printf("%s", text.c_str());
+ fflush(stdout);
+ }
+}
+
+void JavaScript::SetDebugger(lldb::DebuggerSP debugger_sp) {
+ m_debugger = debugger_sp;
+
+ if (!debugger_sp)
+ return;
+
+ v8::Isolate::Scope isolate_scope(m_isolate);
+ v8::HandleScope handle_scope(m_isolate);
+ v8::Local<v8::Context> context = m_context->Get(m_isolate);
+ v8::Context::Scope context_scope(context);
+
+ v8::Local<v8::Value> lldb_val;
+ if (!context->Global()
+ ->Get(context,
+ v8::String::NewFromUtf8(m_isolate, "lldb").ToLocalChecked())
+ .ToLocal(&lldb_val) ||
+ !lldb_val->IsObject())
+ return;
+
+ v8::Local<v8::Object> lldb_obj = lldb_val.As<v8::Object>();
+
+ std::string js_code = llvm::formatv("lldb.SBDebugger.FindDebuggerWithID({0})",
+ debugger_sp->GetID())
+ .str();
+
+ v8::TryCatch try_catch(m_isolate);
+ v8::Local<v8::String> source =
+ v8::String::NewFromUtf8(m_isolate, js_code.c_str(),
+ v8::NewStringType::kNormal, js_code.length())
+ .ToLocalChecked();
+
+ v8::Local<v8::Script> script;
+ if (!v8::Script::Compile(context, source).ToLocal(&script)) {
+ lldb_obj
+ ->Set(context,
+ v8::String::NewFromUtf8(m_isolate, "debugger").ToLocalChecked(),
+ v8::Null(m_isolate))
+ .Check();
+ return;
+ }
+
+ v8::Local<v8::Value> debugger_obj;
+ if (!script->Run(context).ToLocal(&debugger_obj)) {
+ lldb_obj
+ ->Set(context,
+ v8::String::NewFromUtf8(m_isolate, "debugger").ToLocalChecked(),
+ v8::Null(m_isolate))
+ .Check();
+ return;
+ }
+
+ lldb_obj
+ ->Set(context,
+ v8::String::NewFromUtf8(m_isolate, "debugger").ToLocalChecked(),
+ debugger_obj)
+ .Check();
+}
diff --git a/lldb/source/Plugins/ScriptInterpreter/JavaScript/JavaScript.h b/lldb/source/Plugins/ScriptInterpreter/JavaScript/JavaScript.h
new file mode 100644
index 0000000000000..ed13034e33969
--- /dev/null
+++ b/lldb/source/Plugins/ScriptInterpreter/JavaScript/JavaScript.h
@@ -0,0 +1,107 @@
+//===-- JavaScript.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 LLVM_LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_JAVASCRIPT_JAVASCRIPT_H
+#define LLVM_LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_JAVASCRIPT_JAVASCRIPT_H
+
+#include "lldb/Utility/StructuredData.h"
+#include "lldb/lldb-forward.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include <functional>
+#include <memory>
+
+// Forward declare V8 types to avoid including V8 headers here
+namespace v8 {
+class Isolate;
+template <class T> class Global;
+class Context;
+class Platform;
+class Function;
+} // namespace v8
+
+namespace lldb_private {
+
+class JavaScript {
+public:
+ JavaScript(lldb::FileSP output_file = nullptr);
+ ~JavaScript();
+
+ // Execute JavaScript code
+ llvm::Error Run(llvm::StringRef code);
+
+ // Set callback for output (used by console.log)
+ using OutputCallback = std::function<void(const std::string &)>;
+ void SetOutputCallback(OutputCallback callback);
+
+ // Load and execute a JavaScript module
+ llvm::Error LoadModule(llvm::StringRef filename);
+
+ // Check syntax without executing
+ llvm::Error CheckSyntax(llvm::StringRef code);
+
+ // Change IO streams
+ llvm::Error ChangeIO(FILE *out, FILE *err);
+
+ // Breakpoint callback support
+ llvm::Error RegisterBreakpointCallback(void *baton,
+ const char *command_body_text);
+
+ llvm::Expected<bool>
+ CallBreakpointCallback(void *baton, lldb::StackFrameSP stop_frame_sp,
+ lldb::BreakpointLocationSP bp_loc_sp,
+ StructuredData::ObjectSP extra_args_sp);
+
+ // Watchpoint callback support
+ llvm::Error RegisterWatchpointCallback(void *baton,
+ const char *command_body_text);
+
+ llvm::Expected<bool> CallWatchpointCallback(void *baton,
+ lldb::StackFrameSP stop_frame_sp,
+ lldb::WatchpointSP wp_sp);
+
+ // Get the V8 isolate (for advanced usage)
+ v8::Isolate *GetIsolate() { return m_isolate; }
+
+ // Get the output file (for console.log implementation)
+ lldb::FileSP GetOutputFile() { return m_output_file; }
+
+ // Set the output file (for routing console.log to the correct stream)
+ void SetOutputFile(lldb::FileSP output_file) { m_output_file = output_file; }
+
+ // Write output (used by console.log)
+ void WriteOutput(const std::string &text);
+
+ // Set the debugger instance (exposes lldb.debugger to scripts)
+ void SetDebugger(lldb::DebuggerSP debugger_sp);
+
+private:
+ static std::unique_ptr<v8::Platform> s_platform;
+ static bool s_platform_initialized;
+
+ v8::Isolate *m_isolate;
+ v8::Global<v8::Context> *m_context;
+
+ FILE *m_stdout;
+ FILE *m_stderr;
+ lldb::FileSP m_output_file;
+ OutputCallback m_output_callback;
+
+ // Map from baton pointer to JavaScript callback function
+ std::map<void *, v8::Global<v8::Function>> m_breakpoint_callbacks;
+ std::map<void *, v8::Global<v8::Function>> m_watchpoint_callbacks;
+
+ lldb::DebuggerSP m_debugger;
+
+ // Initialize V8 platform (called once)
+ static void InitializePlatform();
+};
+
+} // namespace lldb_private
+
+#endif // LLVM_LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_JAVASCRIPT_JAVASCRIPT_H
diff --git a/lldb/source/Plugins/ScriptInterpreter/JavaScript/SWIGJavaScriptBridge.h b/lldb/source/Plugins/ScriptInterpreter/JavaScript/SWIGJavaScriptBridge.h
new file mode 100644
index 0000000000000..8e2a8579a70b5
--- /dev/null
+++ b/lldb/source/Plugins/ScriptInterpreter/JavaScript/SWIGJavaScriptBridge.h
@@ -0,0 +1,51 @@
+//===-- SWIGJavaScriptBridge.h --------------------------------------------===//
+//
+// 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 LLVM_LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_JAVASCRIPT_SWIGJAVASCRIPTBRIDGE_H
+#define LLVM_LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_JAVASCRIPT_SWIGJAVASCRIPTBRIDGE_H
+
+#include "lldb/lldb-forward.h"
+#include "llvm/Support/Error.h"
+
+namespace lldb_private {
+class StructuredDataImpl;
+} // namespace lldb_private
+
+namespace v8 {
+class Isolate;
+} // namespace v8
+
+// This will be implemented by SWIG-generated code
+extern "C" {
+void init_lldb(v8::Isolate *isolate);
+}
+
+namespace javascript {
+
+// Bridge functions for calling LLDB from JavaScript
+// These will be generated/implemented by SWIG
+namespace SWIGBridge {
+
+// TODO: Implement bridge functions
+// These are similar to LuaBridge functions but for JavaScript/V8
+
+llvm::Expected<bool> LLDBSwigJavaScriptBreakpointCallbackFunction(
+ v8::Isolate *isolate, lldb::StackFrameSP stop_frame_sp,
+ lldb::BreakpointLocationSP bp_loc_sp,
+ const lldb_private::StructuredDataImpl &extra_args_impl);
+
+llvm::Expected<bool>
+LLDBSwigJavaScriptWatchpointCallbackFunction(v8::Isolate *isolate,
+ lldb::StackFrameSP stop_frame_sp,
+ lldb::WatchpointSP wp_sp);
+
+} // namespace SWIGBridge
+
+} // namespace javascript
+
+#endif // LLVM_LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_JAVASCRIPT_SWIGJAVASCRIPTBRIDGE_H
diff --git a/lldb/source/Plugins/ScriptInterpreter/JavaScript/ScriptInterpreterJavaScript.cpp b/lldb/source/Plugins/ScriptInterpreter/JavaScript/ScriptInterpreterJavaScript.cpp
new file mode 100644
index 0000000000000..78f9d9faa502f
--- /dev/null
+++ b/lldb/source/Plugins/ScriptInterpreter/JavaScript/ScriptInterpreterJavaScript.cpp
@@ -0,0 +1,278 @@
+//===-- ScriptInterpreterJavaScript.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 "ScriptInterpreterJavaScript.h"
+#include "JavaScript.h"
+
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/IOHandler.h"
+#include "lldb/Core/PluginManager.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/Support/FormatVariadic.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+LLDB_PLUGIN_DEFINE(ScriptInterpreterJavaScript)
+
+// IOHandler for JavaScript REPL
+class IOHandlerJavaScriptInterpreter : public IOHandlerDelegate,
+ public IOHandlerEditline {
+public:
+ IOHandlerJavaScriptInterpreter(
+ Debugger &debugger, ScriptInterpreterJavaScript &script_interpreter)
+ : IOHandlerEditline(debugger, IOHandler::Type::Other, "javascript",
+ "> ", // Prompt
+ llvm::StringRef(), // No continuation prompt
+ false, // Single-line for now
+ debugger.GetUseColor(), 0, *this),
+ m_script_interpreter(script_interpreter) {
+ llvm::cantFail(m_script_interpreter.EnterSession(debugger.GetID()));
+ }
+
+ ~IOHandlerJavaScriptInterpreter() override {
+ llvm::cantFail(m_script_interpreter.LeaveSession());
+ }
+
+ void IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &data) override {
+ if (data == "quit" || data == "exit") {
+ io_handler.SetIsDone(true);
+ return;
+ }
+
+ // Execute the JavaScript code
+ llvm::Error error = m_script_interpreter.GetJavaScript().Run(data);
+
+ if (error) {
+ // Print error
+ if (LockableStreamFileSP error_sp = io_handler.GetErrorStreamFileSP()) {
+ LockedStreamFile locked_stream = error_sp->Lock();
+ locked_stream << "error: " << llvm::toString(std::move(error)) << "\n";
+ }
+ }
+ }
+
+private:
+ ScriptInterpreterJavaScript &m_script_interpreter;
+};
+
+ScriptInterpreterJavaScript::ScriptInterpreterJavaScript(Debugger &debugger)
+ : ScriptInterpreter(debugger, eScriptLanguageJavaScript),
+ m_javascript(std::make_unique<JavaScript>(debugger.GetOutputFileSP())) {}
+
+ScriptInterpreterJavaScript::~ScriptInterpreterJavaScript() = default;
+
+bool ScriptInterpreterJavaScript::ExecuteOneLine(
+ llvm::StringRef command, CommandReturnObject *result,
+ const ExecuteScriptOptions &options) {
+ if (command.empty()) {
+ if (result)
+ result->AppendError("Empty command string\n");
+ return false;
+ }
+
+ // Set output callback to write console.log output to the result stream
+ if (result) {
+ m_javascript->SetOutputCallback([result](const std::string &text) {
+ result->GetOutputStream().Printf("%s", text.c_str());
+ });
+ }
+
+ llvm::Error error = m_javascript->Run(command);
+
+ // Clear the callback after execution
+ m_javascript->SetOutputCallback(nullptr);
+
+ if (error) {
+ if (result)
+ result->AppendError(llvm::toString(std::move(error)));
+ return false;
+ }
+
+ if (result)
+ result->SetStatus(eReturnStatusSuccessFinishResult);
+ return true;
+}
+
+void ScriptInterpreterJavaScript::ExecuteInterpreterLoop() {
+ LLDB_SCOPED_TIMER();
+
+ if (!m_debugger.GetInputFile().IsValid())
+ return;
+
+ IOHandlerSP io_handler_sp(
+ new IOHandlerJavaScriptInterpreter(m_debugger, *this));
+ m_debugger.RunIOHandlerAsync(io_handler_sp);
+}
+
+bool ScriptInterpreterJavaScript::LoadScriptingModule(
+ const char *filename, const LoadScriptOptions &options,
+ lldb_private::Status &error, StructuredData::ObjectSP *module_sp,
+ FileSpec extra_search_dir, lldb::TargetSP loaded_into_target_sp) {
+
+ if (!filename || filename[0] == '\0') {
+ error = Status::FromErrorString("Empty filename");
+ return false;
+ }
+
+ llvm::Error session_error = EnterSession(m_debugger.GetID());
+ if (session_error) {
+ error = Status::FromErrorString(
+ llvm::toString(std::move(session_error)).c_str());
+ return false;
+ }
+
+ llvm::Error load_error = m_javascript->LoadModule(filename);
+ if (load_error) {
+ error =
+ Status::FromErrorString(llvm::toString(std::move(load_error)).c_str());
+ return false;
+ }
+
+ return true;
+}
+
+StructuredData::DictionarySP ScriptInterpreterJavaScript::GetInterpreterInfo() {
+ auto info_dict = std::make_shared<StructuredData::Dictionary>();
+ info_dict->AddStringItem("language", "javascript");
+ info_dict->AddStringItem("version", "ES2020+ (V8)");
+ return info_dict;
+}
+
+void ScriptInterpreterJavaScript::Initialize() {
+ static llvm::once_flag g_once_flag;
+ llvm::call_once(g_once_flag, []() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), GetPluginDescriptionStatic(),
+ lldb::eScriptLanguageJavaScript, CreateInstance);
+ });
+}
+
+void ScriptInterpreterJavaScript::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+lldb::ScriptInterpreterSP
+ScriptInterpreterJavaScript::CreateInstance(Debugger &debugger) {
+ return std::make_shared<ScriptInterpreterJavaScript>(debugger);
+}
+
+llvm::StringRef ScriptInterpreterJavaScript::GetPluginDescriptionStatic() {
+ return "JavaScript script interpreter";
+}
+
+JavaScript &ScriptInterpreterJavaScript::GetJavaScript() {
+ return *m_javascript;
+}
+
+llvm::Error
+ScriptInterpreterJavaScript::EnterSession(lldb::user_id_t debugger_id) {
+ if (m_session_is_active)
+ return llvm::Error::success();
+
+ m_javascript->SetDebugger(m_debugger.shared_from_this());
+ m_session_is_active = true;
+ return llvm::Error::success();
+}
+
+llvm::Error ScriptInterpreterJavaScript::LeaveSession() {
+ if (!m_session_is_active)
+ return llvm::Error::success();
+
+ m_session_is_active = false;
+ return llvm::Error::success();
+}
+
+void ScriptInterpreterJavaScript::CollectDataForBreakpointCommandCallback(
+ std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
+ CommandReturnObject &result) {
+ result.AppendError("Breakpoint callbacks not yet implemented for JavaScript");
+}
+
+void ScriptInterpreterJavaScript::CollectDataForWatchpointCommandCallback(
+ WatchpointOptions *wp_options, CommandReturnObject &result) {
+ result.AppendError("Watchpoint callbacks not yet implemented for JavaScript");
+}
+
+Status ScriptInterpreterJavaScript::SetBreakpointCommandCallback(
+ BreakpointOptions &bp_options, const char *command_body_text,
+ bool is_callback) {
+ return Status::FromErrorString(
+ "Breakpoint callbacks not yet implemented for JavaScript");
+}
+
+void ScriptInterpreterJavaScript::SetWatchpointCommandCallback(
+ WatchpointOptions *wp_options, const char *command_body_text,
+ bool is_callback) {
+ // TODO: Implement
+}
+
+Status ScriptInterpreterJavaScript::SetBreakpointCommandCallbackFunction(
+ BreakpointOptions &bp_options, const char *function_name,
+ StructuredData::ObjectSP extra_args_sp) {
+ const char *fmt_str = "({0})";
+ std::string oneliner = llvm::formatv(fmt_str, function_name).str();
+
+ auto data_up = std::make_unique<CommandDataJavaScript>(extra_args_sp);
+
+ llvm::Error err =
+ m_javascript->RegisterBreakpointCallback(data_up.get(), oneliner.c_str());
+ if (err)
+ return Status::FromError(std::move(err));
+
+ auto baton_sp =
+ std::make_shared<BreakpointOptions::CommandBaton>(std::move(data_up));
+ bp_options.SetCallback(
+ ScriptInterpreterJavaScript::BreakpointCallbackFunction, baton_sp);
+
+ return Status();
+}
+
+bool ScriptInterpreterJavaScript::BreakpointCallbackFunction(
+ void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id) {
+
+ ExecutionContext exe_ctx(context->exe_ctx_ref);
+ Target *target = exe_ctx.GetTargetPtr();
+ if (!target)
+ 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();
+ ScriptInterpreterJavaScript *js_interpreter =
+ static_cast<ScriptInterpreterJavaScript *>(
+ debugger.GetScriptInterpreter(true, eScriptLanguageJavaScript));
+ JavaScript &js = js_interpreter->GetJavaScript();
+
+ CommandDataJavaScript *bp_option_data =
+ static_cast<CommandDataJavaScript *>(baton);
+ llvm::Expected<bool> BoolOrErr =
+ js.CallBreakpointCallback(baton, stop_frame_sp, bp_loc_sp,
+ bp_option_data->m_extra_args.GetObjectSP());
+ if (llvm::Error E = BoolOrErr.takeError()) {
+ llvm::consumeError(std::move(E));
+ return true;
+ }
+
+ return *BoolOrErr;
+}
+
+bool ScriptInterpreterJavaScript::WatchpointCallbackFunction(
+ void * /*baton*/, StoppointCallbackContext * /*context*/,
+ lldb::user_id_t /*watch_id*/) {
+ return false;
+}
diff --git a/lldb/source/Plugins/ScriptInterpreter/JavaScript/ScriptInterpreterJavaScript.h b/lldb/source/Plugins/ScriptInterpreter/JavaScript/ScriptInterpreterJavaScript.h
new file mode 100644
index 0000000000000..40555d6c0fdca
--- /dev/null
+++ b/lldb/source/Plugins/ScriptInterpreter/JavaScript/ScriptInterpreterJavaScript.h
@@ -0,0 +1,121 @@
+//===-- ScriptInterpreterJavaScript.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 LLVM_LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_JAVASCRIPT_SCRIPTINTERPRETERJAVASCRIPT_H
+#define LLVM_LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_JAVASCRIPT_SCRIPTINTERPRETERJAVASCRIPT_H
+
+#include <memory>
+#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 v8 {
+class Isolate;
+template <class T> class Global;
+class Context;
+class Platform;
+} // namespace v8
+
+namespace lldb_private {
+
+class JavaScript;
+
+class ScriptInterpreterJavaScript : public ScriptInterpreter {
+public:
+ class CommandDataJavaScript : public BreakpointOptions::CommandData {
+ public:
+ CommandDataJavaScript() : BreakpointOptions::CommandData() {
+ interpreter = lldb::eScriptLanguageJavaScript;
+ }
+ CommandDataJavaScript(StructuredData::ObjectSP extra_args_sp)
+ : BreakpointOptions::CommandData(),
+ m_extra_args(std::move(extra_args_sp)) {
+ interpreter = lldb::eScriptLanguageJavaScript;
+ }
+ StructuredDataImpl m_extra_args;
+ };
+
+ ScriptInterpreterJavaScript(Debugger &debugger);
+
+ ~ScriptInterpreterJavaScript() 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 = {},
+ lldb::TargetSP loaded_into_target_sp = {}) override;
+
+ StructuredData::DictionarySP GetInterpreterInfo() override;
+
+ // Static Functions
+ static void Initialize();
+
+ static void Terminate();
+
+ static lldb::ScriptInterpreterSP CreateInstance(Debugger &debugger);
+
+ static llvm::StringRef GetPluginNameStatic() { return "script-javascript"; }
+
+ static llvm::StringRef 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
+ llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+
+ JavaScript &GetJavaScript();
+
+ 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,
+ bool is_callback) override;
+
+ void SetWatchpointCommandCallback(WatchpointOptions *wp_options,
+ const char *command_body_text,
+ bool is_callback) override;
+
+ Status SetBreakpointCommandCallbackFunction(
+ BreakpointOptions &bp_options, const char *function_name,
+ StructuredData::ObjectSP extra_args_sp) override;
+
+private:
+ std::unique_ptr<JavaScript> m_javascript;
+ bool m_session_is_active = false;
+};
+
+} // namespace lldb_private
+
+#endif // LLVM_LLDB_SOURCE_PLUGINS_SCRIPTINTERPRETER_JAVASCRIPT_SCRIPTINTERPRETERJAVASCRIPT_H
More information about the lldb-commits
mailing list