[lldb][Expression] Add API to set/get language-specific expression options (#179208)
The motivation here is that we don't want to pollute the SBAPI with getters/setters for expression evaluation options that only apply to a single language. The ultimate goal would be to have plugins register additional options to the `expression` command when the plugin is loaded. This patch only provides the minimal `SBExpressionOptions` interface to set an option with an arbitrary name, which the language plugin knows how to interpret. The underlying options dictionary is an `StructuredData::Dictionary` so we can map strings to values of any type. But the SBAPI just exposes setting a boolean value. Future overloads of `SetLanguageOption` can provide setters for more types. The boolean setter/getter will be used for the C++-specific option being introduced in: https://github.com/llvm/llvm-project/pull/177926
This commit is contained in:
parent
c51a758d7f
commit
2e52de5aa2
@ -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 currently doesn't validate whether the option being set is understood by the expression evaluator."
|
||||
) lldb::SBExpressionOptions::SetBooleanLanguageOption;
|
||||
|
||||
%feature("docstring", "Gets language-plugin specific boolean option for expression evaluation. LLDB currently doesn't validate whether the option being retrieved is one that is understood by the expression evaluator."
|
||||
) lldb::SBExpressionOptions::GetBooleanLanguageOption;
|
||||
|
||||
@ -107,6 +107,10 @@ public:
|
||||
// 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;
|
||||
|
||||
|
||||
@ -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 @@ private:
|
||||
|
||||
class EvaluateExpressionOptions {
|
||||
public:
|
||||
EvaluateExpressionOptions()
|
||||
: m_language_options_sp(std::make_shared<StructuredData::Dictionary>()) {}
|
||||
|
||||
// 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 @@ public:
|
||||
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 @@ public:
|
||||
|
||||
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<bool>
|
||||
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 @@ private:
|
||||
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).
|
||||
|
||||
@ -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,38 @@ void SBExpressionOptions::SetAllowJIT(bool allow) {
|
||||
: eExecutionPolicyNever);
|
||||
}
|
||||
|
||||
// FIXME: the language plugin should expression options dynamically and
|
||||
// we should validate here (by asking the language plugin) that the options
|
||||
// being set/retrieved are actually valid options.
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@ -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 <memory>
|
||||
@ -5357,3 +5358,54 @@ void Target::NotifyBreakpointChanged(
|
||||
if (EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged))
|
||||
BroadcastEvent(Target::eBroadcastBitBreakpointChanged, breakpoint_data_sp);
|
||||
}
|
||||
|
||||
// FIXME: the language plugin should expression options dynamically and
|
||||
// we should validate here (by asking the language plugin) that the options
|
||||
// being set/retrieved are actually valid options.
|
||||
|
||||
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.");
|
||||
|
||||
if (StructuredData::ObjectSP existing_sp =
|
||||
GetLanguageOptions().GetValueForKey(option_name);
|
||||
existing_sp && existing_sp->GetType() != eStructuredDataTypeBoolean)
|
||||
return llvm::createStringErrorV("Trying to override existing option '{0}' "
|
||||
"of type '{1}' with a boolean value.",
|
||||
option_name, existing_sp->GetType());
|
||||
|
||||
GetLanguageOptions().AddBooleanItem(option_name, value);
|
||||
|
||||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
llvm::Expected<bool> 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;
|
||||
}
|
||||
|
||||
@ -7,7 +7,6 @@ o test_expr_options:
|
||||
Test expression command options.
|
||||
"""
|
||||
|
||||
|
||||
import lldb
|
||||
import lldbsuite.test.lldbutil as lldbutil
|
||||
from lldbsuite.test.decorators import *
|
||||
@ -85,3 +84,38 @@ class ExprOptionsTestCase(TestBase):
|
||||
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())
|
||||
|
||||
@ -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."));
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user