[Lldb-commits] [lldb] [lldb] Add evaluation modes to DIL (PR #178747)
Ilia Kuklin via lldb-commits
lldb-commits at lists.llvm.org
Thu Feb 12 09:51:25 PST 2026
https://github.com/kuilpd updated https://github.com/llvm/llvm-project/pull/178747
>From e606ad113488a752ca36e270ebb02424a69e8e33 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Fri, 30 Jan 2026 01:02:50 +0500
Subject: [PATCH 1/3] [lldb] Add evaluation modes to DIL
DIL will only attempt evaluating expressions that contain tokens
allowed by a selected mode:
- Simple: identifiers, operators: '.'
- Legacy: identifiers, integers, operators: '.', '->', '*', '&', '[]'
- Full: everything supported by DIL
---
lldb/include/lldb/API/SBFrame.h | 10 +++++---
lldb/include/lldb/Target/BorrowedStackFrame.h | 3 ++-
lldb/include/lldb/Target/StackFrame.h | 10 ++++++--
lldb/include/lldb/ValueObject/DILLexer.h | 4 +++-
lldb/include/lldb/lldb-enumerations.h | 13 ++++++++++
lldb/source/API/SBFrame.cpp | 10 ++++----
.../Commands/CommandObjectDWIMPrint.cpp | 4 ++--
.../Process/scripted/ScriptedFrame.cpp | 3 ++-
.../Plugins/Process/scripted/ScriptedFrame.h | 3 ++-
lldb/source/Target/BorrowedStackFrame.cpp | 4 ++--
lldb/source/Target/StackFrame.cpp | 9 +++----
lldb/source/ValueObject/DILLexer.cpp | 24 +++++++++++++++++--
.../AddressOf/TestFrameVarDILAddressOf.py | 9 ++++++-
.../TestFrameVarDILArraySubscript.py | 9 ++++++-
.../LocalVars/TestFrameVarDILLocalVars.py | 9 ++++++-
.../MemberOf/TestFrameVarDILMemberOf.py | 22 +++++++++++++----
.../TestFrameVarDILPointerDereference.py | 9 ++++++-
.../var-dil/expr/Casts/TestFrameVarDILCast.py | 7 ++++++
.../lldb-dap/evaluate/TestDAP_evaluate.py | 1 +
.../Handler/EvaluateRequestHandler.cpp | 4 ++--
lldb/tools/lldb-dap/SourceBreakpoint.cpp | 4 ++--
21 files changed, 136 insertions(+), 35 deletions(-)
diff --git a/lldb/include/lldb/API/SBFrame.h b/lldb/include/lldb/API/SBFrame.h
index 5283cdfe53faa..eaf9a4bfece96 100644
--- a/lldb/include/lldb/API/SBFrame.h
+++ b/lldb/include/lldb/API/SBFrame.h
@@ -182,12 +182,16 @@ class LLDB_API SBFrame {
// expression result and is not a constant object like
// SBFrame::EvaluateExpression(...) returns, but a child object of the
// variable value.
- lldb::SBValue GetValueForVariablePath(const char *var_expr_cstr,
- DynamicValueType use_dynamic);
+ lldb::SBValue
+ GetValueForVariablePath(const char *var_expr_cstr,
+ DynamicValueType use_dynamic,
+ lldb::DILMode mode = lldb::eDILModeFull);
/// The version that doesn't supply a 'use_dynamic' value will use the
/// target's default.
- lldb::SBValue GetValueForVariablePath(const char *var_path);
+ lldb::SBValue
+ GetValueForVariablePath(const char *var_path,
+ lldb::DILMode mode = lldb::eDILModeFull);
/// Find variables, register sets, registers, or persistent variables using
/// the frame as the scope.
diff --git a/lldb/include/lldb/Target/BorrowedStackFrame.h b/lldb/include/lldb/Target/BorrowedStackFrame.h
index 72e7777961da7..2a34f2816aed2 100644
--- a/lldb/include/lldb/Target/BorrowedStackFrame.h
+++ b/lldb/include/lldb/Target/BorrowedStackFrame.h
@@ -86,7 +86,8 @@ class BorrowedStackFrame : public StackFrame {
lldb::ValueObjectSP GetValueForVariableExpressionPath(
llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
- uint32_t options, lldb::VariableSP &var_sp, Status &error) override;
+ uint32_t options, lldb::VariableSP &var_sp, Status &error,
+ lldb::DILMode mode = lldb::eDILModeFull) override;
bool HasDebugInformation() override;
diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h
index 5cba9afe2a7e8..c114cd2a13fdf 100644
--- a/lldb/include/lldb/Target/StackFrame.h
+++ b/lldb/include/lldb/Target/StackFrame.h
@@ -316,11 +316,16 @@ class StackFrame : public ExecutionContextScope,
/// \param[in] error
/// Record any errors encountered while evaluating var_expr.
///
+ /// \param[in] mode
+ /// Data Inspection Language (DIL) evaluation mode.
+ /// \see lldb::DILMode
+ ///
/// \return
/// A shared pointer to the ValueObject described by var_expr.
virtual lldb::ValueObjectSP GetValueForVariableExpressionPath(
llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
- uint32_t options, lldb::VariableSP &var_sp, Status &error);
+ uint32_t options, lldb::VariableSP &var_sp, Status &error,
+ lldb::DILMode mode = lldb::eDILModeFull);
/// Determine whether this StackFrame has debug information available or not.
///
@@ -615,7 +620,8 @@ class StackFrame : public ExecutionContextScope,
lldb::ValueObjectSP DILGetValueForVariableExpressionPath(
llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
- uint32_t options, lldb::VariableSP &var_sp, Status &error);
+ uint32_t options, lldb::VariableSP &var_sp, Status &error,
+ lldb::DILMode mode = lldb::eDILModeFull);
StackFrame(const StackFrame &) = delete;
const StackFrame &operator=(const StackFrame &) = delete;
diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h
index 47b117de7b80d..a927aa236377e 100644
--- a/lldb/include/lldb/ValueObject/DILLexer.h
+++ b/lldb/include/lldb/ValueObject/DILLexer.h
@@ -9,6 +9,7 @@
#ifndef LLDB_VALUEOBJECT_DILLEXER_H
#define LLDB_VALUEOBJECT_DILLEXER_H
+#include "lldb/lldb-enumerations.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FormatVariadic.h"
@@ -74,7 +75,8 @@ class DILLexer {
public:
/// Lexes all the tokens in expr and calls the private constructor
/// with the lexed tokens.
- static llvm::Expected<DILLexer> Create(llvm::StringRef expr);
+ static llvm::Expected<DILLexer>
+ Create(llvm::StringRef expr, lldb::DILMode mode = lldb::eDILModeFull);
/// Return the current token to be handled by the DIL parser.
const Token &GetCurrentToken() { return m_lexed_tokens[m_tokens_idx]; }
diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h
index 4cbbabbf879ad..67600c8bb4248 100644
--- a/lldb/include/lldb/lldb-enumerations.h
+++ b/lldb/include/lldb/lldb-enumerations.h
@@ -1420,6 +1420,19 @@ enum NameMatchStyle {
eNameMatchStyleRegex = eFunctionNameTypeSelector << 1
};
+/// Data Inspection Language (DIL) evaluation modes.
+/// DIL will only attempt evaluating expressions that contain tokens
+/// allowed by a selected mode.
+enum DILMode {
+ /// Allowed: identifiers, operators: '.'.
+ eDILModeSimple,
+ /// Allowed: identifiers, integers, operators: '.', '->', '*', '&', '[]'.
+ eDILModeLegacy,
+ /// Allowed: everything supported by DIL.
+ /// \see lldb/docs/dil-expr-lang.ebnf
+ eDILModeFull
+};
+
} // namespace lldb
#endif // LLDB_LLDB_ENUMERATIONS_H
diff --git a/lldb/source/API/SBFrame.cpp b/lldb/source/API/SBFrame.cpp
index 31947a054aaaf..c0f932cc2c43b 100644
--- a/lldb/source/API/SBFrame.cpp
+++ b/lldb/source/API/SBFrame.cpp
@@ -363,7 +363,8 @@ void SBFrame::Clear() {
m_opaque_sp->Clear();
}
-lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path) {
+lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path,
+ lldb::DILMode mode) {
LLDB_INSTRUMENT_VA(this, var_path);
SBValue sb_value;
@@ -377,13 +378,14 @@ lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path) {
if (StackFrame *frame = exe_ctx->GetFramePtr()) {
lldb::DynamicValueType use_dynamic =
frame->CalculateTarget()->GetPreferDynamicValue();
- sb_value = GetValueForVariablePath(var_path, use_dynamic);
+ sb_value = GetValueForVariablePath(var_path, use_dynamic, mode);
}
return sb_value;
}
lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path,
- DynamicValueType use_dynamic) {
+ DynamicValueType use_dynamic,
+ lldb::DILMode mode) {
LLDB_INSTRUMENT_VA(this, var_path, use_dynamic);
SBValue sb_value;
@@ -405,7 +407,7 @@ lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path,
var_path, eNoDynamicValues,
StackFrame::eExpressionPathOptionCheckPtrVsMember |
StackFrame::eExpressionPathOptionsAllowDirectIVarAccess,
- var_sp, error));
+ var_sp, error, mode));
sb_value.SetSP(value_sp, use_dynamic);
}
return sb_value;
diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.cpp b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
index 40f00c90bbbfb..27bd71c21ad3f 100644
--- a/lldb/source/Commands/CommandObjectDWIMPrint.cpp
+++ b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
@@ -170,8 +170,8 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
Status status;
auto valobj_sp = frame->GetValueForVariableExpressionPath(
expr, eval_options.GetUseDynamic(),
- StackFrame::eExpressionPathOptionsAllowDirectIVarAccess, var_sp,
- status);
+ StackFrame::eExpressionPathOptionsAllowDirectIVarAccess, var_sp, status,
+ lldb::eDILModeSimple);
if (valobj_sp && status.Success() && valobj_sp->GetError().Success()) {
if (!suppress_result) {
if (auto persisted_valobj = valobj_sp->Persist())
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp
index ab7f70efb4bd4..54b65dcaed873 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp
+++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp
@@ -316,7 +316,8 @@ lldb::ValueObjectSP ScriptedFrame::GetValueObjectForFrameVariable(
lldb::ValueObjectSP ScriptedFrame::GetValueForVariableExpressionPath(
llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
- uint32_t options, lldb::VariableSP &var_sp, Status &error) {
+ uint32_t options, lldb::VariableSP &var_sp, Status &error,
+ lldb::DILMode mode) {
// Unless the frame implementation knows how to create variables (which it
// doesn't), we can't construct anything for the variable. This may seem
// somewhat out of place, but it's basically because of how this API is used -
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.h b/lldb/source/Plugins/Process/scripted/ScriptedFrame.h
index fe154792c745b..e4c57831c2576 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedFrame.h
+++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.h
@@ -76,7 +76,8 @@ class ScriptedFrame : public lldb_private::StackFrame {
lldb::ValueObjectSP GetValueForVariableExpressionPath(
llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
- uint32_t options, lldb::VariableSP &var_sp, Status &error) override;
+ uint32_t options, lldb::VariableSP &var_sp, Status &error,
+ lldb::DILMode mode = lldb::eDILModeFull) override;
bool isA(const void *ClassID) const override {
return ClassID == &ID || StackFrame::isA(ClassID);
diff --git a/lldb/source/Target/BorrowedStackFrame.cpp b/lldb/source/Target/BorrowedStackFrame.cpp
index 5afadf21fde03..5b81a1f05bbfb 100644
--- a/lldb/source/Target/BorrowedStackFrame.cpp
+++ b/lldb/source/Target/BorrowedStackFrame.cpp
@@ -99,9 +99,9 @@ BorrowedStackFrame::GetInScopeVariableList(bool get_file_globals,
ValueObjectSP BorrowedStackFrame::GetValueForVariableExpressionPath(
llvm::StringRef var_expr, DynamicValueType use_dynamic, uint32_t options,
- VariableSP &var_sp, Status &error) {
+ VariableSP &var_sp, Status &error, lldb::DILMode mode) {
return m_borrowed_frame_sp->GetValueForVariableExpressionPath(
- var_expr, use_dynamic, options, var_sp, error);
+ var_expr, use_dynamic, options, var_sp, error, mode);
}
bool BorrowedStackFrame::HasDebugInformation() {
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index 340607e14abed..9c80e8c0b8ccf 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -524,13 +524,13 @@ StackFrame::GetInScopeVariableList(bool get_file_globals,
ValueObjectSP StackFrame::GetValueForVariableExpressionPath(
llvm::StringRef var_expr, DynamicValueType use_dynamic, uint32_t options,
- VariableSP &var_sp, Status &error) {
+ VariableSP &var_sp, Status &error, lldb::DILMode mode) {
ExecutionContext exe_ctx;
CalculateExecutionContext(exe_ctx);
bool use_DIL = exe_ctx.GetTargetRef().GetUseDIL(&exe_ctx);
if (use_DIL)
return DILGetValueForVariableExpressionPath(var_expr, use_dynamic, options,
- var_sp, error);
+ var_sp, error, mode);
return LegacyGetValueForVariableExpressionPath(var_expr, use_dynamic, options,
var_sp, error);
@@ -538,7 +538,8 @@ ValueObjectSP StackFrame::GetValueForVariableExpressionPath(
ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
- uint32_t options, lldb::VariableSP &var_sp, Status &error) {
+ uint32_t options, lldb::VariableSP &var_sp, Status &error,
+ lldb::DILMode mode) {
const bool check_ptr_vs_member =
(options & eExpressionPathOptionCheckPtrVsMember) != 0;
@@ -548,7 +549,7 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
(options & eExpressionPathOptionsNoSyntheticChildren) != 0;
// Lex the expression.
- auto lex_or_err = dil::DILLexer::Create(var_expr);
+ auto lex_or_err = dil::DILLexer::Create(var_expr, mode);
if (!lex_or_err) {
error = Status::FromError(lex_or_err.takeError());
return ValueObjectConstResult::Create(nullptr, std::move(error));
diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp
index 72c97f7bf272b..dc5508cbf706b 100644
--- a/lldb/source/ValueObject/DILLexer.cpp
+++ b/lldb/source/ValueObject/DILLexer.cpp
@@ -111,12 +111,32 @@ static std::optional<llvm::StringRef> IsNumber(llvm::StringRef &remainder,
return std::nullopt;
}
-llvm::Expected<DILLexer> DILLexer::Create(llvm::StringRef expr) {
+llvm::Expected<DILLexer> DILLexer::Create(llvm::StringRef expr,
+ lldb::DILMode mode) {
std::vector<Token> tokens;
llvm::StringRef remainder = expr;
do {
if (llvm::Expected<Token> t = Lex(expr, remainder)) {
- tokens.push_back(std::move(*t));
+ Token token = *t;
+ if (mode == lldb::eDILModeSimple &&
+ !token.IsOneOf({Token::identifier, Token::period, Token::eof})) {
+ std::string errMsg =
+ llvm::formatv("token '{0}' is not allowed in DIL simple mode",
+ Token::GetTokenName(token.GetKind()));
+ return llvm::make_error<DILDiagnosticError>(expr, errMsg,
+ token.GetLocation());
+ } else if (mode == lldb::eDILModeLegacy &&
+ !token.IsOneOf({Token::identifier, Token::integer_constant,
+ Token::period, Token::arrow, Token::star,
+ Token::amp, Token::l_square, Token::r_square,
+ Token::eof})) {
+ std::string errMsg =
+ llvm::formatv("token '{0}' is not allowed in DIL legacy mode",
+ Token::GetTokenName(token.GetKind()));
+ return llvm::make_error<DILDiagnosticError>(expr, errMsg,
+ token.GetLocation());
+ }
+ tokens.push_back(std::move(token));
} else {
return t.takeError();
}
diff --git a/lldb/test/API/commands/frame/var-dil/basics/AddressOf/TestFrameVarDILAddressOf.py b/lldb/test/API/commands/frame/var-dil/basics/AddressOf/TestFrameVarDILAddressOf.py
index 8eab75949047d..bfe29370b4704 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/AddressOf/TestFrameVarDILAddressOf.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/AddressOf/TestFrameVarDILAddressOf.py
@@ -21,7 +21,7 @@ def expect_var_path(self, expr, compare_to_framevar=False, value=None, type=None
def test_frame_var(self):
self.build()
- lldbutil.run_to_source_breakpoint(
+ (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
)
@@ -36,3 +36,10 @@ def test_frame_var(self):
self.expect_var_path("&globalVar", True, type="int *")
self.expect_var_path("&s_str", True, type="const char **")
self.expect_var_path("&argc", True, type="int *")
+
+ # Check that '&' is not allowed in simple mode, but allowed in legacy mode
+ frame = thread.GetFrameAtIndex(0)
+ simple = frame.GetValueForVariablePath("&x", lldb.eDILModeSimple)
+ legacy = frame.GetValueForVariablePath("&x", lldb.eDILModeLegacy)
+ self.assertFailure(simple.GetError())
+ self.assertSuccess(legacy.GetError())
diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py
index b22a445e603cd..6f3bbfb970aa4 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py
@@ -13,7 +13,7 @@ class TestFrameVarDILArraySubscript(TestBase):
def test_subscript(self):
self.build()
- lldbutil.run_to_source_breakpoint(
+ (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
)
@@ -87,6 +87,13 @@ def test_subscript(self):
substrs=["subscript of pointer to incomplete type 'void'"],
)
+ # Check that subscription is not allowed in simple mode, but allowed in legacy mode
+ frame = thread.GetFrameAtIndex(0)
+ simple = frame.GetValueForVariablePath("int_arr[0]", lldb.eDILModeSimple)
+ legacy = frame.GetValueForVariablePath("int_arr[0]", lldb.eDILModeLegacy)
+ self.assertFailure(simple.GetError())
+ self.assertSuccess(legacy.GetError())
+
def test_subscript_synthetic(self):
self.build()
lldbutil.run_to_source_breakpoint(
diff --git a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
index b082d977a3a99..79d8d7abd4671 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
@@ -16,7 +16,7 @@ class TestFrameVarDILLocalVars(TestBase):
def test_frame_var(self):
self.build()
- lldbutil.run_to_source_breakpoint(
+ (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
)
@@ -25,3 +25,10 @@ def test_frame_var(self):
self.expect_var_path("b", value="2")
self.expect_var_path("c", value="'\\xfd'")
self.expect_var_path("s", value="4")
+
+ # Check that identifiers are allowed in both simple and legacy modes
+ frame = thread.GetFrameAtIndex(0)
+ simple = frame.GetValueForVariablePath("a", lldb.eDILModeSimple)
+ legacy = frame.GetValueForVariablePath("a", lldb.eDILModeLegacy)
+ self.assertSuccess(simple.GetError())
+ self.assertSuccess(legacy.GetError())
diff --git a/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py
index ca6754a556d89..d37ce0bbc4201 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/MemberOf/TestFrameVarDILMemberOf.py
@@ -11,6 +11,7 @@
import shutil
import time
+
class TestFrameVarDILMemberOf(TestBase):
# If your test case doesn't stress debug info, then
# set this to true. That way it won't be run once for
@@ -19,11 +20,11 @@ class TestFrameVarDILMemberOf(TestBase):
def test_frame_var(self):
self.build()
- lldbutil.run_to_source_breakpoint(self, "Set a breakpoint here",
- lldb.SBFileSpec("main.cpp"))
+ (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
+ self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+ )
- self.expect("settings set target.experimental.use-DIL true",
- substrs=[""])
+ self.expect("settings set target.experimental.use-DIL true", substrs=[""])
self.expect_var_path("s.x", value="1")
self.expect_var_path("s.r", type="int &")
self.expect_var_path("sr.x", value="1")
@@ -48,3 +49,16 @@ def test_frame_var(self):
# Test for record typedefs.
self.expect_var_path("sa.x", value="3")
self.expect_var_path("sa.y", value="'\\x04'")
+
+ # Check that '.' is allowed in both simple and legacy modes
+ frame = thread.GetFrameAtIndex(0)
+ simple = frame.GetValueForVariablePath("s.x", lldb.eDILModeSimple)
+ legacy = frame.GetValueForVariablePath("s.x", lldb.eDILModeLegacy)
+ self.assertSuccess(simple.GetError())
+ self.assertSuccess(legacy.GetError())
+
+ # Check that '->' is not allowed in simple mode, but allowed in legacy mode
+ simple = frame.GetValueForVariablePath("sp->x", lldb.eDILModeSimple)
+ legacy = frame.GetValueForVariablePath("sp->x", lldb.eDILModeLegacy)
+ self.assertFailure(simple.GetError())
+ self.assertSuccess(legacy.GetError())
diff --git a/lldb/test/API/commands/frame/var-dil/basics/PointerDereference/TestFrameVarDILPointerDereference.py b/lldb/test/API/commands/frame/var-dil/basics/PointerDereference/TestFrameVarDILPointerDereference.py
index ffb447441e982..e18e18e146a46 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/PointerDereference/TestFrameVarDILPointerDereference.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/PointerDereference/TestFrameVarDILPointerDereference.py
@@ -17,7 +17,7 @@ class TestFrameVarDILPointerDereference(TestBase):
def test_frame_var(self):
self.build()
- lldbutil.run_to_source_breakpoint(
+ (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
)
@@ -46,3 +46,10 @@ def test_frame_var(self):
pp_int0_2stars_got.GetValueAsAddress(),
pp_int0_2stars_exp.GetValueAsAddress(),
)
+
+ # Check that * is not allowed in simple mode, but allowed in legacy mode
+ frame = thread.GetFrameAtIndex(0)
+ simple = frame.GetValueForVariablePath("*p_int0", lldb.eDILModeSimple)
+ legacy = frame.GetValueForVariablePath("*p_int0", lldb.eDILModeLegacy)
+ self.assertFailure(simple.GetError())
+ self.assertSuccess(legacy.GetError())
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py b/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py
index 6e83622ef9fb9..50affd4ccb03e 100644
--- a/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py
+++ b/lldb/test/API/commands/frame/var-dil/expr/Casts/TestFrameVarDILCast.py
@@ -285,3 +285,10 @@ def test_type_cast(self):
error=True,
substrs=["expected 'eof', got: <'InnerFoo' (identifier)>"],
)
+
+ # Check that casts are not allowed in both simple and legacy modes
+ frame = thread.GetFrameAtIndex(0)
+ simple = frame.GetValueForVariablePath("(char)a", lldb.eDILModeSimple)
+ legacy = frame.GetValueForVariablePath("(char)a", lldb.eDILModeLegacy)
+ self.assertFailure(simple.GetError())
+ self.assertFailure(legacy.GetError())
diff --git a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
index 98ed0b9df228a..556168e5adfa8 100644
--- a/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
+++ b/lldb/test/API/tools/lldb-dap/evaluate/TestDAP_evaluate.py
@@ -253,6 +253,7 @@ def run_test_evaluate_expressions(
self.assertEvaluateFailure("a_function(1)")
self.assertEvaluateFailure("var2 + struct1.foo")
self.assertEvaluateFailure("foo_func")
+ self.assertEvaluateFailure("(float) var2")
self.assertEvaluate("foo_var", "44")
# Expressions at breakpoint 2, which is an anonymous block
diff --git a/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
index 82a011b8088df..44a5be5c17b20 100644
--- a/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp
@@ -54,8 +54,8 @@ static lldb::SBValue EvaluateVariableExpression(lldb::SBTarget &target,
// Check if it is a variable or an expression path for a variable. i.e.
// 'foo->bar' finds the 'bar' variable. It is more reliable than the
// expression parser in many cases and it is faster.
- value = frame.GetValueForVariablePath(expression_cstr,
- lldb::eDynamicDontRunTarget);
+ value = frame.GetValueForVariablePath(
+ expression_cstr, lldb::eDynamicDontRunTarget, lldb::eDILModeLegacy);
if (value || !run_as_expression)
return value;
diff --git a/lldb/tools/lldb-dap/SourceBreakpoint.cpp b/lldb/tools/lldb-dap/SourceBreakpoint.cpp
index 6f776f4d5f429..233dbff81f337 100644
--- a/lldb/tools/lldb-dap/SourceBreakpoint.cpp
+++ b/lldb/tools/lldb-dap/SourceBreakpoint.cpp
@@ -397,8 +397,8 @@ bool SourceBreakpoint::BreakpointHitCallback(
// evaluation
const std::string &expr_str = messagePart.text;
const char *expr = expr_str.c_str();
- lldb::SBValue value =
- frame.GetValueForVariablePath(expr, lldb::eDynamicDontRunTarget);
+ lldb::SBValue value = frame.GetValueForVariablePath(
+ expr, lldb::eDynamicDontRunTarget, lldb::eDILModeLegacy);
if (value.GetError().Fail())
value = frame.EvaluateExpression(expr);
output += VariableDescription(
>From 3eca690db908f51343209442bea640e3efd87d45 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Thu, 5 Feb 2026 20:42:37 +0500
Subject: [PATCH 2/3] Move token checking to a separate function
---
lldb/source/ValueObject/DILLexer.cpp | 43 ++++++++++++++++------------
1 file changed, 25 insertions(+), 18 deletions(-)
diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp
index dc5508cbf706b..7936e71c9210a 100644
--- a/lldb/source/ValueObject/DILLexer.cpp
+++ b/lldb/source/ValueObject/DILLexer.cpp
@@ -111,6 +111,29 @@ static std::optional<llvm::StringRef> IsNumber(llvm::StringRef &remainder,
return std::nullopt;
}
+static llvm::Error IsNotAllowedByMode(llvm::StringRef expr, Token token,
+ lldb::DILMode mode) {
+ if (mode == lldb::eDILModeSimple &&
+ !token.IsOneOf({Token::identifier, Token::period, Token::eof})) {
+ std::string errMsg =
+ llvm::formatv("token '{0}' is not allowed in DIL simple mode",
+ Token::GetTokenName(token.GetKind()));
+ return llvm::make_error<DILDiagnosticError>(expr, errMsg,
+ token.GetLocation());
+ } else if (mode == lldb::eDILModeLegacy &&
+ !token.IsOneOf({Token::identifier, Token::integer_constant,
+ Token::period, Token::arrow, Token::star,
+ Token::amp, Token::l_square, Token::r_square,
+ Token::eof})) {
+ std::string errMsg =
+ llvm::formatv("token '{0}' is not allowed in DIL legacy mode",
+ Token::GetTokenName(token.GetKind()));
+ return llvm::make_error<DILDiagnosticError>(expr, errMsg,
+ token.GetLocation());
+ }
+ return llvm::Error::success();
+}
+
llvm::Expected<DILLexer> DILLexer::Create(llvm::StringRef expr,
lldb::DILMode mode) {
std::vector<Token> tokens;
@@ -118,24 +141,8 @@ llvm::Expected<DILLexer> DILLexer::Create(llvm::StringRef expr,
do {
if (llvm::Expected<Token> t = Lex(expr, remainder)) {
Token token = *t;
- if (mode == lldb::eDILModeSimple &&
- !token.IsOneOf({Token::identifier, Token::period, Token::eof})) {
- std::string errMsg =
- llvm::formatv("token '{0}' is not allowed in DIL simple mode",
- Token::GetTokenName(token.GetKind()));
- return llvm::make_error<DILDiagnosticError>(expr, errMsg,
- token.GetLocation());
- } else if (mode == lldb::eDILModeLegacy &&
- !token.IsOneOf({Token::identifier, Token::integer_constant,
- Token::period, Token::arrow, Token::star,
- Token::amp, Token::l_square, Token::r_square,
- Token::eof})) {
- std::string errMsg =
- llvm::formatv("token '{0}' is not allowed in DIL legacy mode",
- Token::GetTokenName(token.GetKind()));
- return llvm::make_error<DILDiagnosticError>(expr, errMsg,
- token.GetLocation());
- }
+ if (llvm::Error error = IsNotAllowedByMode(expr, token, mode))
+ return error;
tokens.push_back(std::move(token));
} else {
return t.takeError();
>From 007c9cd9cddbdcba97df41bf164fb47bc848d540 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Tue, 10 Feb 2026 15:42:27 +0500
Subject: [PATCH 3/3] Check modes in a switch statement
---
lldb/source/ValueObject/DILLexer.cpp | 36 +++++++++++++++-------------
1 file changed, 19 insertions(+), 17 deletions(-)
diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp
index 7936e71c9210a..c1ad354502438 100644
--- a/lldb/source/ValueObject/DILLexer.cpp
+++ b/lldb/source/ValueObject/DILLexer.cpp
@@ -113,23 +113,25 @@ static std::optional<llvm::StringRef> IsNumber(llvm::StringRef &remainder,
static llvm::Error IsNotAllowedByMode(llvm::StringRef expr, Token token,
lldb::DILMode mode) {
- if (mode == lldb::eDILModeSimple &&
- !token.IsOneOf({Token::identifier, Token::period, Token::eof})) {
- std::string errMsg =
- llvm::formatv("token '{0}' is not allowed in DIL simple mode",
- Token::GetTokenName(token.GetKind()));
- return llvm::make_error<DILDiagnosticError>(expr, errMsg,
- token.GetLocation());
- } else if (mode == lldb::eDILModeLegacy &&
- !token.IsOneOf({Token::identifier, Token::integer_constant,
- Token::period, Token::arrow, Token::star,
- Token::amp, Token::l_square, Token::r_square,
- Token::eof})) {
- std::string errMsg =
- llvm::formatv("token '{0}' is not allowed in DIL legacy mode",
- Token::GetTokenName(token.GetKind()));
- return llvm::make_error<DILDiagnosticError>(expr, errMsg,
- token.GetLocation());
+ switch (mode) {
+ case lldb::eDILModeSimple:
+ if (!token.IsOneOf({Token::identifier, Token::period, Token::eof})) {
+ return llvm::make_error<DILDiagnosticError>(
+ expr, llvm::formatv("{0} is not allowed in DIL simple mode", token),
+ token.GetLocation());
+ }
+ break;
+ case lldb::eDILModeLegacy:
+ if (!token.IsOneOf({Token::identifier, Token::integer_constant,
+ Token::period, Token::arrow, Token::star, Token::amp,
+ Token::l_square, Token::r_square, Token::eof})) {
+ return llvm::make_error<DILDiagnosticError>(
+ expr, llvm::formatv("{0} is not allowed in DIL legacy mode", token),
+ token.GetLocation());
+ }
+ break;
+ case lldb::eDILModeFull:
+ break;
}
return llvm::Error::success();
}
More information about the lldb-commits
mailing list