Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lldb/bindings/interface/SBExpressionOptionsDocstrings.i
Original file line number Diff line number Diff line change
Expand Up @@ -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;
4 changes: 4 additions & 0 deletions lldb/include/lldb/API/SBExpressionOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Copy link
Member

@medismailben medismailben Feb 2, 2026

Choose a reason for hiding this comment

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

it's weird to a getter specifically for boolean options but have the setter name be "generic" but take a boolean argument. I also think this is going to break in Python if you add overloads for the setter with different argument types like void SetLanguageOption(const char *option_name, int value).

Suggested change
bool GetBooleanLanguageOption(const char *option_name) const;
void SetBooleanLanguageOption(const char *option_name, bool value);

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh my assumption was that we can overload functions. Pretty sure SWIG is capable of it? But do we avoid doing overloads?

Copy link
Member

Choose a reason for hiding this comment

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

You can overload functions. SWIG will handle it correctly with args or kwargs in python.

I think Ismail's point is one of consistency. The getter has Boolean in the name but the setter doesn't (even though the two methods are supposed to be matching). I think changing the setter name would resolve this.

To go further, does GetLanguageOptionAsBoolean mean you can get a non-boolean option value as a boolean? Maybe something like GetBooleanLanguageOption is clearer? To make it matchy-matchy you'd also want SetBooleanLanguageOption.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sounds good, I'll rename the two as suggested!

protected:
lldb_private::EvaluateExpressionOptions *get() const;

Expand Down
25 changes: 23 additions & 2 deletions lldb/include/lldb/Target/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -307,6 +308,9 @@ class TargetProperties : public Properties {

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
Expand All @@ -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) {
Expand Down Expand Up @@ -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<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;
Expand Down Expand Up @@ -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).
Expand Down
29 changes: 29 additions & 0 deletions lldb/source/API/SBExpressionOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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();
}
Expand Down
41 changes: 41 additions & 0 deletions lldb/source/Target/Target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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);
Copy link
Collaborator

Choose a reason for hiding this comment

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

There doesn't seem to be any system yet for the Language to validate the options you are setting in it. It would be a stretch to require you to put that machinery in place just for this option. But we should at least have a big FIXME here because we really should come up with a way to do that. And we should probably add a note to the documentation that at present we don't validate the spelling of options set this way.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Other than that, this LGTM.


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;
}
36 changes: 35 additions & 1 deletion lldb/test/API/commands/expression/options/TestExprOptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
Test expression command options.
"""


import lldb
import lldbsuite.test.lldbutil as lldbutil
from lldbsuite.test.decorators import *
Expand Down Expand Up @@ -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())
39 changes: 39 additions & 0 deletions lldb/unittests/Expression/ExpressionTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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