Skip to content

Conversation

@cs01
Copy link
Contributor

@cs01 cs01 commented Oct 30, 2025

Adds optional JavaScript scripting support to LLDB via V8. Users must install V8 separately and enable with -DLLDB_ENABLE_JAVASCRIPT=ON. Scripts access the LLDB API through a global lldb object and can run interactively or from files. Includes SWIG bindings, CMake detection for V8, and documentation. Some things are still missing things like breakpoint callback support.

For build instructions, check out the branch and see the file lldb/docs/use/javascript-reference.md.

For discussion, see #165807.

To try this out save the following file

/tmp/test.js:

lldb.debugger.SetAsync(false);
let target = lldb.debugger.CreateTarget("/bin/ls");

let breakpoint = target.BreakpointCreateByName("main");
console.log("Breakpoint ID:", breakpoint.GetID());

let error = new lldb.SBError();
let proc = target.Launch(target.GetLaunchInfo(), error);

if (error.Fail()) {
    console.error("Error:", error.GetCString());
} else {
    console.log("PID:", proc.GetProcessID());

    let thread = proc.GetSelectedThread();
    console.log("Thread:", thread.GetThreadID());
    console.log("Stop reason:", thread.GetStopReason());

    console.log("\nStack trace:");
    for (let i = 0; i < Math.min(thread.GetNumFrames(), 4); i++) {
        let frame = thread.GetFrameAtIndex(i);
        let pc = frame.GetPCAddress().GetLoadAddress(target);
        let pcHex = "0x" + (pc >>> 0).toString(16).padStart(8, '0');
        console.log("Frame #" + i + ": " + pcHex);
    }

    console.log("Registers:");
    let frame0 = thread.GetFrameAtIndex(0);
    let gprs = frame0.GetRegisters().GetValueAtIndex(0);

    for (let i = 0; i < Math.min(gprs.GetNumChildren(), 10); i++) {
        let reg = gprs.GetChildAtIndex(i);
        console.log(reg.GetName() + " = " + reg.GetValue());
    }

    proc.Kill();
}

undefined;

then run

./build/bin/lldb -b -o 'settings set script-lang javascript' -o 'command script import /tmp/test.js'

which prints

bin/lldb -b -o 'settings set script-lang javascript' -o 'command script import /tmp/test_process_launch.js'
(lldb) settings set script-lang javascript
(lldb) command script import /tmp/test_process_launch.js
Breakpoint ID: 1
PID: 2351277
Thread: 2351277
Stop reason: 3

Stack trace:
Frame #0: 0x55558d80
Frame #1: 0xf7c2a610
Frame #2: 0xf7c2a6c0
Frame #3: 0x5555ab35

Registers:
rax = 0x0000555555558d80
rbx = 0x0000000000000000
rcx = 0x0000555555574f78
rdx = 0x00007fffffffcbf8
rdi = 0x0000000000000001
rsi = 0x00007fffffffcbe8
rbp = 0x0000000000000001
rsp = 0x00007fffffffcad8
r8 = 0x00007ffff7dfc330
r9 = 0x00007ffff7fcb060
> 

@cs01 cs01 requested a review from JDevlieghere as a code owner October 30, 2025 23:49
@llvmbot llvmbot added the lldb label Oct 30, 2025
@llvmbot
Copy link
Member

llvmbot commented Oct 30, 2025

@llvm/pr-subscribers-lldb

Author: Chad Smith (cs01)

Changes

Adds optional JavaScript scripting support to LLDB via V8. Users must install V8 separately and enable with -DLLDB_ENABLE_JAVASCRIPT=ON. Scripts access the LLDB API through a global lldb object and can run interactively or from files. Includes SWIG bindings, CMake detection for V8, and documentation. Some things are still missing things like breakpoint callback support.

To try this out save the following file

/tmp/test.js:

let dbg = lldb.SBDebugger.Create();
dbg.SetAsync(false);
let target = dbg.CreateTarget("/bin/ls");

let breakpoint = target.BreakpointCreateByName("main");
console.log("Breakpoint ID:", breakpoint.GetID());

let error = new lldb.SBError();
let proc = target.Launch(target.GetLaunchInfo(), error);

if (error.Fail()) {
    console.error("Error:", error.GetCString());
} else {
    console.log("PID:", proc.GetProcessID());

    let thread = proc.GetSelectedThread();
    console.log("Thread:", thread.GetThreadID());
    console.log("Stop reason:", thread.GetStopReason());

    console.log("\nStack trace:");
    for (let i = 0; i &lt; Math.min(thread.GetNumFrames(), 4); i++) {
        let frame = thread.GetFrameAtIndex(i);
        let pc = frame.GetPCAddress().GetLoadAddress(target);
        let pcHex = "0x" + (pc &gt;&gt;&gt; 0).toString(16).padStart(8, '0');
        console.log("Frame #" + i + ": " + pcHex);
    }

    console.log("Registers:");
    let frame0 = thread.GetFrameAtIndex(0);
    let gprs = frame0.GetRegisters().GetValueAtIndex(0);

    for (let i = 0; i &lt; Math.min(gprs.GetNumChildren(), 10); i++) {
        let reg = gprs.GetChildAtIndex(i);
        console.log(reg.GetName() + " = " + reg.GetValue());
    }

    proc.Kill();
}

undefined;

then run

./build/bin/lldb -b -o 'settings set script-lang javascript' -o 'command script import /tmp/test.js'

which prints

bin/lldb -b -o 'settings set script-lang javascript' -o 'command script import /tmp/test_process_launch.js'
(lldb) settings set script-lang javascript
(lldb) command script import /tmp/test_process_launch.js
Breakpoint ID: 1
PID: 2351277
Thread: 2351277
Stop reason: 3

Stack trace:
Frame #<!-- -->0: 0x55558d80
Frame #<!-- -->1: 0xf7c2a610
Frame #<!-- -->2: 0xf7c2a6c0
Frame #<!-- -->3: 0x5555ab35

Registers:
rax = 0x0000555555558d80
rbx = 0x0000000000000000
rcx = 0x0000555555574f78
rdx = 0x00007fffffffcbf8
rdi = 0x0000000000000001
rsi = 0x00007fffffffcbe8
rbp = 0x0000000000000001
rsp = 0x00007fffffffcad8
r8 = 0x00007ffff7dfc330
r9 = 0x00007ffff7fcb060
&gt; 

Patch is 55.17 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/165805.diff

23 Files Affected:

  • (modified) lldb/CMakeLists.txt (+11-1)
  • (modified) lldb/bindings/CMakeLists.txt (+4)
  • (added) lldb/bindings/javascript/CMakeLists.txt (+69)
  • (added) lldb/bindings/javascript/javascript-swigsafecast.swig (+8)
  • (added) lldb/bindings/javascript/javascript-typemaps.swig (+13)
  • (added) lldb/bindings/javascript/javascript-wrapper.swig (+12)
  • (added) lldb/bindings/javascript/javascript.swig (+30)
  • (added) lldb/cmake/modules/FindV8.cmake (+71)
  • (modified) lldb/cmake/modules/LLDBConfig.cmake (+1)
  • (modified) lldb/docs/index.rst (+4-1)
  • (modified) lldb/docs/resources/build.rst (+2)
  • (added) lldb/docs/use/javascript-reference.md (+263)
  • (modified) lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h (+5)
  • (modified) lldb/include/lldb/lldb-enumerations.h (+11-2)
  • (modified) lldb/source/Core/Debugger.cpp (+5)
  • (modified) lldb/source/Interpreter/ScriptInterpreter.cpp (+4)
  • (modified) lldb/source/Plugins/ScriptInterpreter/CMakeLists.txt (+4)
  • (added) lldb/source/Plugins/ScriptInterpreter/JavaScript/CMakeLists.txt (+40)
  • (added) lldb/source/Plugins/ScriptInterpreter/JavaScript/JavaScript.cpp (+373)
  • (added) lldb/source/Plugins/ScriptInterpreter/JavaScript/JavaScript.h (+106)
  • (added) lldb/source/Plugins/ScriptInterpreter/JavaScript/SWIGJavaScriptBridge.h (+53)
  • (added) lldb/source/Plugins/ScriptInterpreter/JavaScript/ScriptInterpreterJavaScript.cpp (+229)
  • (added) lldb/source/Plugins/ScriptInterpreter/JavaScript/ScriptInterpreterJavaScript.h (+108)
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..209089b4d3fcb
--- /dev/null
+++ b/lldb/bindings/javascript/javascript-typemaps.swig
@@ -0,0 +1,13 @@
+/*
+   JavaScript-specific typemaps for LLDB
+*/
+
+// Basic type mappings for JavaScript/V8
+// SWIG's JavaScript module handles most standard types automatically,
+// but we may need custom mappings for LLDB-specific types.
+
+// TODO: Add custom typemaps as needed for:
+// - File handles
+// - Callbacks
+// - Error handling
+// - Memory buffers
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/Interpret...
[truncated]

@cs01 cs01 marked this pull request as draft October 30, 2025 23:53
@cs01 cs01 force-pushed the cs01/lldb-support-javascript branch from abd0d94 to c228953 Compare October 31, 2025 15:24
@JDevlieghere JDevlieghere requested a review from Copilot October 31, 2025 20:16
Copy link
Member

@JDevlieghere JDevlieghere left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few quick issues when scrolling through the PR.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds JavaScript scripting support to LLDB through integration with the V8 JavaScript engine. JavaScript becomes the third supported scripting language alongside Python and Lua, providing an alternative for developers who prefer JavaScript syntax and semantics.

Key changes:

  • New ScriptInterpreterJavaScript plugin with V8 engine integration
  • JavaScript REPL and script execution support via V8
  • Breakpoint callback infrastructure (partial implementation)
  • CMake configuration for optional JavaScript support

Reviewed Changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 17 comments.

Show a summary per file
File Description
ScriptInterpreterJavaScript.h/cpp Core plugin implementing JavaScript interpreter interface
JavaScript.h/cpp V8 engine wrapper handling execution, callbacks, and console output
SWIGJavaScriptBridge.h Bridge function declarations for SWIG-generated bindings
lldb-enumerations.h Added eScriptLanguageJavaScript enumeration and default language fallback
ScriptInterpreter.cpp Added JavaScript language string conversion support
Debugger.cpp Registered JavaScript as selectable scripting language
CommandOptionArgumentTable.h Added JavaScript to script option enumerations
CMakeLists.txt (various) Build system integration for JavaScript plugin
FindV8.cmake CMake module to locate V8 library
javascript.swig and related SWIG bindings configuration for JavaScript
javascript-reference.md Documentation for JavaScript scripting support
build.rst, index.rst Documentation updates

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 234 to 237
#elif LLDB_ENABLE_JAVASCRIPT
eScriptLanguageDefault = eScriptLanguageJavaScript
#elif LLDB_ENABLE_LUA
eScriptLanguageDefault = eScriptLanguageLua
Copy link

Copilot AI Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fallback order for eScriptLanguageDefault places JavaScript before Lua. This changes the existing priority where Lua was the fallback after Python. This could break existing behavior for users who have Python disabled but rely on Lua as the default. Consider maintaining the original fallback order: Python → Lua → JavaScript → None.

Suggested change
#elif LLDB_ENABLE_JAVASCRIPT
eScriptLanguageDefault = eScriptLanguageJavaScript
#elif LLDB_ENABLE_LUA
eScriptLanguageDefault = eScriptLanguageLua
#elif LLDB_ENABLE_LUA
eScriptLanguageDefault = eScriptLanguageLua
#elif LLDB_ENABLE_JAVASCRIPT
eScriptLanguageDefault = eScriptLanguageJavaScript

Copilot uses AI. Check for mistakes.
@JDevlieghere
Copy link
Member

I added the Copilot reviewer to test drive #165783

@cs01 cs01 force-pushed the cs01/lldb-support-javascript branch from c228953 to e062204 Compare November 2, 2025 03:09
@cs01
Copy link
Contributor Author

cs01 commented Nov 3, 2025

The CI check failed with this. Was this an existing issue? I didn't this would impact the python scripting test, but maybe it did.

https://github.com/llvm/llvm-project/actions/runs/19006500551/job/54280874261?pr=165805#step:3:9124

 # executed command: /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/lldb --no-lldbinit -S /home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/lldb/test/Shell/lit-lldb-init-quiet -s /home/gha/actions-runner/_work/llvm-project/llvm-project/lldb/test/Shell/Commands/command-scripting-extension-list.test -o exit
  # note: command had no output on stdout or stderr
  # executed command: /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/FileCheck /home/gha/actions-runner/_work/llvm-project/llvm-project/lldb/test/Shell/Commands/command-scripting-extension-list.test
  # .---command stderr------------
  # | /home/gha/actions-runner/_work/llvm-project/llvm-project/lldb/test/Shell/Commands/command-scripting-extension-list.test:7:10: error: CHECK: expected string not found in input
  # | # CHECK: Name: OperatingSystemPythonInterface
  # |          ^
  # | <stdin>:7:40: note: scanning from here
  # | Available scripted extension templates: None
  # |                                        ^
  # | <stdin>:8:2: note: possible intended match here
  # | (lldb) scripting extension list -l lua
  # |  ^
  # | 
  # | Input file: <stdin>
  # | Check file: /home/gha/actions-runner/_work/llvm-project/llvm-project/lldb/test/Shell/Commands/command-scripting-extension-list.test
  # | 
  # | -dump-input=help explains the following input dump.
  # | 
  # | Input was:
  # | <<<<<<
  # |            1: (lldb) command source -s 0 '/home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/lldb/test/Shell/lit-lldb-init-quiet' 
  # |            2: Executing commands in '/home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/lldb/test/Shell/lit-lldb-init-quiet'. 
  # |            3: (lldb) command source -C --silent-run true lit-lldb-init 
  # |            4: (lldb) command source -s 0 '/home/gha/actions-runner/_work/llvm-project/llvm-project/lldb/test/Shell/Commands/command-scripting-extension-list.test' 
  # |            5: Executing commands in '/home/gha/actions-runner/_work/llvm-project/llvm-project/lldb/test/Shell/Commands/command-scripting-extension-list.test'. 
  # |            6: (lldb) scripting extension list 
  # |            7: Available scripted extension templates: None 
  # | check:7'0                                            X~~~~~ error: no match found
  # |            8: (lldb) scripting extension list -l lua 
  # | check:7'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  # | check:7'1      ?                                      possible intended match
  # |            9: Available scripted extension templates: None 
  # | check:7'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  # |           10: (lldb) exit 
  # | check:7'0     ~~~~~~~~~~~~
  # | >>>>>>
  # `-----------------------------
  # error: command failed with exit status: 1
  
  --
 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants