diff --git a/lldb/bindings/interface/SBExpressionOptionsDocstrings.i b/lldb/bindings/interface/SBExpressionOptionsDocstrings.i index 2bb562778db79..ae897a6aa5bcd 100644 --- a/lldb/bindings/interface/SBExpressionOptionsDocstrings.i +++ b/lldb/bindings/interface/SBExpressionOptionsDocstrings.i @@ -61,3 +61,9 @@ %feature("docstring", "Sets whether to JIT an expression if it cannot be interpreted." ) lldb::SBExpressionOptions::SetAllowJIT; + +%feature("docstring", "Sets language-plugin specific boolean option for expression evaluation." +) lldb::SBExpressionOptions::SetBooleanLanguageOption; + +%feature("docstring", "Gets language-plugin specific boolean option for expression evaluation." +) lldb::SBExpressionOptions::GetBooleanLanguageOption; diff --git a/lldb/include/lldb/API/SBExpressionOptions.h b/lldb/include/lldb/API/SBExpressionOptions.h index a9e929a4c0bd9..edfdbb5aaf62f 100644 --- a/lldb/include/lldb/API/SBExpressionOptions.h +++ b/lldb/include/lldb/API/SBExpressionOptions.h @@ -107,6 +107,10 @@ class LLDB_API SBExpressionOptions { // Sets whether we will JIT an expression if it cannot be interpreted void SetAllowJIT(bool allow); + bool GetBooleanLanguageOption(const char *option_name, SBError &error) const; + + SBError SetBooleanLanguageOption(const char *option_name, bool value); + protected: lldb_private::EvaluateExpressionOptions *get() const; diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h index 812a638910b3b..a79d0f1b6113e 100644 --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -38,6 +38,7 @@ #include "lldb/Utility/Broadcaster.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/RealpathPrefixes.h" +#include "lldb/Utility/StructuredData.h" #include "lldb/Utility/Timeout.h" #include "lldb/lldb-public.h" #include "llvm/ADT/StringRef.h" @@ -307,6 +308,9 @@ class TargetProperties : public Properties { class EvaluateExpressionOptions { public: + EvaluateExpressionOptions() + : m_language_options_sp(std::make_shared()) {} + // MSVC has a bug here that reports C4268: 'const' static/global data // initialized with compiler generated default constructor fills the object // with zeros. Confirmed that MSVC is *not* zero-initializing, it's just a @@ -323,8 +327,6 @@ class EvaluateExpressionOptions { static constexpr ExecutionPolicy default_execution_policy = eExecutionPolicyOnlyWhenNeeded; - EvaluateExpressionOptions() = default; - ExecutionPolicy GetExecutionPolicy() const { return m_execution_policy; } void SetExecutionPolicy(ExecutionPolicy policy = eExecutionPolicyAlways) { @@ -481,7 +483,22 @@ class EvaluateExpressionOptions { void SetIsForUtilityExpr(bool b) { m_running_utility_expression = b; } + /// Set language-plugin specific option called \c option_name to + /// the specified boolean \c value. + llvm::Error SetBooleanLanguageOption(llvm::StringRef option_name, bool value); + + /// Get the language-plugin specific boolean option called \c option_name. + /// + /// If the option doesn't exist or is not a boolean option, returns false. + /// Otherwise returns the boolean value of the option. + llvm::Expected + GetBooleanLanguageOption(llvm::StringRef option_name) const; + private: + const StructuredData::Dictionary &GetLanguageOptions() const; + + StructuredData::Dictionary &GetLanguageOptions(); + ExecutionPolicy m_execution_policy = default_execution_policy; SourceLanguage m_language; std::string m_prefix; @@ -514,6 +531,10 @@ class EvaluateExpressionOptions { mutable std::string m_pound_line_file; mutable uint32_t m_pound_line_line = 0; + /// Dictionary mapping names of language-plugin specific options + /// to values. + StructuredData::DictionarySP m_language_options_sp = nullptr; + /// During expression evaluation, any SymbolContext in this list will be /// used for symbol/function lookup before any other context (except for /// the module corresponding to the current frame). diff --git a/lldb/source/API/SBExpressionOptions.cpp b/lldb/source/API/SBExpressionOptions.cpp index 15ed403eaaea1..908f69a4cba3f 100644 --- a/lldb/source/API/SBExpressionOptions.cpp +++ b/lldb/source/API/SBExpressionOptions.cpp @@ -8,6 +8,7 @@ #include "lldb/API/SBExpressionOptions.h" #include "Utils.h" +#include "lldb/API/SBError.h" #include "lldb/API/SBStream.h" #include "lldb/Target/Target.h" #include "lldb/Utility/Instrumentation.h" @@ -256,6 +257,34 @@ void SBExpressionOptions::SetAllowJIT(bool allow) { : eExecutionPolicyNever); } +bool SBExpressionOptions::GetBooleanLanguageOption(const char *option_name, + SBError &error) const { + LLDB_INSTRUMENT_VA(this, option_name, error); + + error.Clear(); + + auto value_or_err = m_opaque_up->GetBooleanLanguageOption(option_name); + if (!value_or_err) { + error.SetErrorString(llvm::toString(value_or_err.takeError()).c_str()); + return false; + } + + return *value_or_err; +} + +SBError SBExpressionOptions::SetBooleanLanguageOption(const char *option_name, + bool value) { + LLDB_INSTRUMENT_VA(this, option_name, value); + + SBError error; + + if (llvm::Error err = + m_opaque_up->SetBooleanLanguageOption(option_name, value)) + error.SetErrorString(llvm::toString(std::move(err)).c_str()); + + return error; +} + EvaluateExpressionOptions *SBExpressionOptions::get() const { return m_opaque_up.get(); } diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index f3e058c6cbc9b..6b6dfa4aebe2b 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -70,6 +70,7 @@ #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SetVector.h" +#include "llvm/Support/ErrorExtras.h" #include "llvm/Support/ThreadPool.h" #include @@ -5357,3 +5358,43 @@ void Target::NotifyBreakpointChanged( if (EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged)) BroadcastEvent(Target::eBroadcastBitBreakpointChanged, breakpoint_data_sp); } + +llvm::Error +EvaluateExpressionOptions::SetBooleanLanguageOption(llvm::StringRef option_name, + bool value) { + if (option_name.empty()) + return llvm::createStringError("Can't set an option with an empty name."); + + GetLanguageOptions().AddBooleanItem(option_name, value); + + return llvm::Error::success(); +} + +llvm::Expected EvaluateExpressionOptions::GetBooleanLanguageOption( + llvm::StringRef option_name) const { + const StructuredData::Dictionary &opts = GetLanguageOptions(); + + if (!opts.HasKey(option_name)) + return llvm::createStringErrorV("Option '{0}' does not exist.", + option_name); + + bool result; + if (!opts.GetValueForKeyAsBoolean(option_name, result)) + return llvm::createStringErrorV("Failed to get option '{0}' as boolean.", + option_name); + + return result; +} + +const StructuredData::Dictionary & +EvaluateExpressionOptions::GetLanguageOptions() const { + assert(m_language_options_sp); + + return *m_language_options_sp; +} + +StructuredData::Dictionary &EvaluateExpressionOptions::GetLanguageOptions() { + assert(m_language_options_sp); + + return *m_language_options_sp; +} diff --git a/lldb/test/API/commands/expression/options/TestExprOptions.py b/lldb/test/API/commands/expression/options/TestExprOptions.py index 01899f3b97cf4..02f844b34cc2b 100644 --- a/lldb/test/API/commands/expression/options/TestExprOptions.py +++ b/lldb/test/API/commands/expression/options/TestExprOptions.py @@ -7,7 +7,6 @@ Test expression command options. """ - import lldb import lldbsuite.test.lldbutil as lldbutil from lldbsuite.test.decorators import * @@ -85,3 +84,38 @@ def test_expr_options_lang(self): val = frame.EvaluateExpression("id == 0", options) self.assertTrue(val.IsValid()) self.assertFalse(val.GetError().Success()) + + def test_expr_options_language_options(self): + """Test SetBooleanLanguageOption/GetBooleanLanguageOption SBAPIs""" + + error = lldb.SBError() + options = lldb.SBExpressionOptions() + + self.assertFalse(options.GetBooleanLanguageOption("foo", error)) + self.assertTrue(error.Fail()) + self.assertFalse(options.GetBooleanLanguageOption("bar", error)) + self.assertTrue(error.Fail()) + + self.assertTrue(options.SetBooleanLanguageOption("foo", True).Success()) + self.assertTrue(options.SetBooleanLanguageOption("bar", True).Success()) + self.assertTrue(options.GetBooleanLanguageOption("foo", error)) + self.assertTrue(error.Success()) + self.assertTrue(options.GetBooleanLanguageOption("bar", error)) + self.assertTrue(error.Success()) + + self.assertTrue(options.SetBooleanLanguageOption("foo", False).Success()) + self.assertTrue(options.SetBooleanLanguageOption("bar", False).Success()) + self.assertFalse(options.GetBooleanLanguageOption("foo", error)) + self.assertTrue(error.Success()) + self.assertFalse(options.GetBooleanLanguageOption("bar", error)) + self.assertTrue(error.Success()) + + self.assertFalse(options.GetBooleanLanguageOption("", error)) + self.assertTrue(error.Fail()) + self.assertTrue(options.SetBooleanLanguageOption("", True).Fail()) + self.assertFalse(options.GetBooleanLanguageOption("", error)) + self.assertTrue(error.Fail()) + + self.assertTrue(options.SetBooleanLanguageOption(None, True).Fail()) + self.assertFalse(options.GetBooleanLanguageOption(None, error)) + self.assertTrue(error.Fail()) diff --git a/lldb/unittests/Expression/ExpressionTest.cpp b/lldb/unittests/Expression/ExpressionTest.cpp index ceb567c28ab99..19647c162fa5a 100644 --- a/lldb/unittests/Expression/ExpressionTest.cpp +++ b/lldb/unittests/Expression/ExpressionTest.cpp @@ -11,6 +11,7 @@ #include "TestingSupport/TestUtilities.h" #include "lldb/Expression/Expression.h" +#include "lldb/Target/Target.h" #include "llvm/Testing/Support/Error.h" using namespace lldb_private; @@ -127,3 +128,41 @@ TEST_P(ExpressionTestFixture, FunctionCallLabel) { INSTANTIATE_TEST_SUITE_P(FunctionCallLabelTest, ExpressionTestFixture, testing::ValuesIn(g_label_test_cases)); + +TEST(ExpressionTests, ExpressionOptions_Basic) { + EvaluateExpressionOptions options; + + EXPECT_THAT_EXPECTED(options.GetBooleanLanguageOption("foo"), + llvm::FailedWithMessage("Option 'foo' does not exist.")); + EXPECT_THAT_EXPECTED(options.GetBooleanLanguageOption("bar"), + llvm::FailedWithMessage("Option 'bar' does not exist.")); + + EXPECT_THAT_ERROR(options.SetBooleanLanguageOption("foo", true), + llvm::Succeeded()); + EXPECT_THAT_ERROR(options.SetBooleanLanguageOption("bar", false), + llvm::Succeeded()); + + EXPECT_THAT_EXPECTED(options.GetBooleanLanguageOption("foo"), + llvm::HasValue(true)); + EXPECT_THAT_EXPECTED(options.GetBooleanLanguageOption("bar"), + llvm::HasValue(false)); + + EXPECT_THAT_ERROR(options.SetBooleanLanguageOption("foo", false), + llvm::Succeeded()); + EXPECT_THAT_ERROR(options.SetBooleanLanguageOption("bar", true), + llvm::Succeeded()); + + EXPECT_THAT_EXPECTED(options.GetBooleanLanguageOption("foo"), + llvm::HasValue(false)); + EXPECT_THAT_EXPECTED(options.GetBooleanLanguageOption("bar"), + llvm::HasValue(true)); + + // Empty option names not allowed. + EXPECT_THAT_EXPECTED(options.GetBooleanLanguageOption(""), + llvm::FailedWithMessage("Option '' does not exist.")); + EXPECT_THAT_ERROR( + options.SetBooleanLanguageOption("", true), + llvm::FailedWithMessage("Can't set an option with an empty name.")); + EXPECT_THAT_EXPECTED(options.GetBooleanLanguageOption(""), + llvm::FailedWithMessage("Option '' does not exist.")); +}