From 80fffd527c20ac8970fbffc37c674caa17faa815 Mon Sep 17 00:00:00 2001 From: Ilia Kuklin Date: Fri, 13 Feb 2026 18:34:58 +0500 Subject: [PATCH] [lldb] Add evaluation modes to DIL (#178747) Adding more supported operators to DIL breaks tests in `DWIMPrint` and `lldb-dap`, which shouldn't be simply adjusted for new DIL capabilities. They act as a check for the boundaries of what subset of expressions `DWIMPrint` and `lldb-dap` expect to be evaluated when using `GetValueForVariableExpressionPath` function. With this patch, the caller can now pick a mode that limits the expressions DIL can evaluate, which ensures the expected preexisting behavior. More operators can now be safely added to DIL, which can still be evaluated by DIL when using `frame var` command or the API call with Full mode selected (or not specified at all). DIL will only attempt evaluating expressions that contain operations 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 | 33 +++++++++++++++++-- .../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, 145 insertions(+), 35 deletions(-) diff --git a/lldb/include/lldb/API/SBFrame.h b/lldb/include/lldb/API/SBFrame.h index 5283cdfe53fa..eaf9a4bfece9 100644 --- a/lldb/include/lldb/API/SBFrame.h +++ b/lldb/include/lldb/API/SBFrame.h @@ -182,12 +182,16 @@ public: // 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 72e7777961da..2a34f2816aed 100644 --- a/lldb/include/lldb/Target/BorrowedStackFrame.h +++ b/lldb/include/lldb/Target/BorrowedStackFrame.h @@ -86,7 +86,8 @@ public: 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 5cba9afe2a7e..c114cd2a13fd 100644 --- a/lldb/include/lldb/Target/StackFrame.h +++ b/lldb/include/lldb/Target/StackFrame.h @@ -316,11 +316,16 @@ public: /// \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 @@ private: 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 47b117de7b80..a927aa236377 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 Create(llvm::StringRef expr); + static llvm::Expected + 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 4cbbabbf879a..67600c8bb424 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 31947a054aaa..c0f932cc2c43 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 40f00c90bbbf..27bd71c21ad3 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 ab7f70efb4bd..54b65dcaed87 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 2bf62c06cdd1..c2fc1df7724d 100644 --- a/lldb/source/Plugins/Process/scripted/ScriptedFrame.h +++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.h @@ -76,7 +76,8 @@ public: 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 5afadf21fde0..5b81a1f05bbf 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 340607e14abe..9c80e8c0b8cc 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 72c97f7bf272..c1ad35450243 100644 --- a/lldb/source/ValueObject/DILLexer.cpp +++ b/lldb/source/ValueObject/DILLexer.cpp @@ -111,12 +111,41 @@ static std::optional IsNumber(llvm::StringRef &remainder, return std::nullopt; } -llvm::Expected DILLexer::Create(llvm::StringRef expr) { +static llvm::Error IsNotAllowedByMode(llvm::StringRef expr, Token token, + lldb::DILMode mode) { + switch (mode) { + case lldb::eDILModeSimple: + if (!token.IsOneOf({Token::identifier, Token::period, Token::eof})) { + return llvm::make_error( + 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( + expr, llvm::formatv("{0} is not allowed in DIL legacy mode", token), + token.GetLocation()); + } + break; + case lldb::eDILModeFull: + break; + } + return llvm::Error::success(); +} + +llvm::Expected DILLexer::Create(llvm::StringRef expr, + lldb::DILMode mode) { std::vector tokens; llvm::StringRef remainder = expr; do { if (llvm::Expected t = Lex(expr, remainder)) { - tokens.push_back(std::move(*t)); + Token token = *t; + if (llvm::Error error = IsNotAllowedByMode(expr, token, mode)) + return error; + 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 8eab75949047..bfe29370b470 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 @@ class TestFrameVarDILGlobalVariableLookup(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") ) @@ -36,3 +36,10 @@ class TestFrameVarDILGlobalVariableLookup(TestBase): 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 b22a445e603c..6f3bbfb970aa 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 @@ class TestFrameVarDILArraySubscript(TestBase): 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 b082d977a3a9..79d8d7abd467 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 @@ class TestFrameVarDILLocalVars(TestBase): 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 ca6754a556d8..d37ce0bbc420 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 os 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 @@ class TestFrameVarDILMemberOf(TestBase): # 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 ffb447441e98..e18e18e146a4 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 @@ class TestFrameVarDILPointerDereference(TestBase): 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 6e83622ef9fb..50affd4ccb03 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 @@ class TestFrameVarDILCast(TestBase): 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 98ed0b9df228..556168e5adfa 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 @@ class TestDAP_evaluate(lldbdap_testcase.DAPTestCaseBase): 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 82a011b8088d..44a5be5c17b2 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 6f776f4d5f42..233dbff81f33 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(