[LLDB] Add allow_var_updates to DIL and CanUpdateVar to SetValueFromInteger (#186421)

In preparation for updating DIL to handle assignments, this adds a
member variable to the DIL Interpreter indicating whether or not
updating program variables is allowed. For invocations from the LLDB
command prompt (through "frame variable") we want to allow it, but from
other places we might not. Therefore we also add new StackFrame
ExpressionPathOption, eExpressionPathOptionsAllowVarUpdates, which we
add to calls from CommandObjectFrame, and which is checked in
GetValueForVariableExpressionPath. Finally, we also add a parameter,
can_update_vars, with a default value of true, to
ValueObject::SetValueFromInteger, as that will be the main function used
to by assignment in DIL.
This commit is contained in:
cmtice 2026-03-31 14:13:48 -07:00 committed by GitHub
parent c1ebd2f1c0
commit f43ee18e98
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 71 additions and 53 deletions

View File

@ -56,7 +56,8 @@ public:
eExpressionPathOptionsNoSyntheticChildren = (1u << 2),
eExpressionPathOptionsNoSyntheticArrayRange = (1u << 3),
eExpressionPathOptionsAllowDirectIVarAccess = (1u << 4),
eExpressionPathOptionsInspectAnonymousUnions = (1u << 5)
eExpressionPathOptionsInspectAnonymousUnions = (1u << 5),
eExpressionPathOptionsAllowVarUpdates = (1u << 6)
};
enum class Kind {

View File

@ -40,8 +40,7 @@ class Interpreter : Visitor {
public:
Interpreter(lldb::TargetSP target, llvm::StringRef expr,
std::shared_ptr<StackFrame> frame_sp,
lldb::DynamicValueType use_dynamic, bool use_synthetic,
bool fragile_ivar, bool check_ptr_vs_member);
lldb::DynamicValueType use_dynamic, uint32_t options);
/// Evaluate an ASTNode.
/// \returns A non-null lldb::ValueObjectSP or an Error.
@ -137,6 +136,8 @@ private:
bool m_use_synthetic;
bool m_fragile_ivar;
bool m_check_ptr_vs_member;
// TODO: Remove 'maybe_unused' when next PR, using this, gets submitted.
[[maybe_unused]] bool m_allow_var_updates;
};
} // namespace lldb_private::dil

View File

@ -71,8 +71,7 @@ public:
DILLexer lexer,
std::shared_ptr<StackFrame> frame_sp,
lldb::DynamicValueType use_dynamic,
bool use_synthetic, bool fragile_ivar,
bool check_ptr_vs_member);
uint32_t options);
~DILParser() = default;

View File

@ -453,17 +453,17 @@ public:
/// value to a boolean and return that. Otherwise return an error.
llvm::Expected<bool> GetValueAsBool();
/// Update an existing integer ValueObject with a new integer value. This
/// is only intended to be used with 'temporary' ValueObjects, i.e. ones that
/// are not associated with program variables. It does not update program
/// memory, registers, stack, etc.
void SetValueFromInteger(const llvm::APInt &value, Status &error);
/// Update an existing integer ValueObject with a new integer value. If
/// can_update_var is true, will allow updating objects associated with
/// program variables; otherwise not.
void SetValueFromInteger(const llvm::APInt &value, Status &error,
bool can_update_var = true);
/// Update an existing integer ValueObject with an integer value created
/// frome 'new_val_sp'. This is only intended to be used with 'temporary'
/// ValueObjects, i.e. ones that are not associated with program variables.
/// It does not update program memory, registers, stack, etc.
void SetValueFromInteger(lldb::ValueObjectSP new_val_sp, Status &error);
/// frome 'new_val_sp'. If can_update_var is true, will allow updating objects
/// associated with program variables; otherwise not.
void SetValueFromInteger(lldb::ValueObjectSP new_val_sp, Status &error,
bool can_update_var = true);
virtual bool SetValueFromCString(const char *value_str, Status &error);

View File

@ -610,7 +610,8 @@ protected:
uint32_t expr_path_options =
StackFrame::eExpressionPathOptionCheckPtrVsMember |
StackFrame::eExpressionPathOptionsAllowDirectIVarAccess |
StackFrame::eExpressionPathOptionsInspectAnonymousUnions;
StackFrame::eExpressionPathOptionsInspectAnonymousUnions |
StackFrame::eExpressionPathOptionsAllowVarUpdates;
lldb::VariableSP var_sp;
valobj_sp = frame->GetValueForVariableExpressionPath(
entry.ref(), m_varobj_options.use_dynamic, expr_path_options,

View File

@ -541,13 +541,6 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
uint32_t options, lldb::VariableSP &var_sp, Status &error,
lldb::DILMode mode) {
const bool check_ptr_vs_member =
(options & eExpressionPathOptionCheckPtrVsMember) != 0;
const bool no_fragile_ivar =
(options & eExpressionPathOptionsNoFragileObjcIvar) != 0;
const bool no_synth_child =
(options & eExpressionPathOptionsNoSyntheticChildren) != 0;
// Lex the expression.
auto lex_or_err = dil::DILLexer::Create(var_expr, mode);
if (!lex_or_err) {
@ -556,9 +549,9 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
}
// Parse the expression.
auto tree_or_error = dil::DILParser::Parse(
var_expr, std::move(*lex_or_err), shared_from_this(), use_dynamic,
!no_synth_child, !no_fragile_ivar, check_ptr_vs_member);
auto tree_or_error =
dil::DILParser::Parse(var_expr, std::move(*lex_or_err),
shared_from_this(), use_dynamic, options);
if (!tree_or_error) {
error = Status::FromError(tree_or_error.takeError());
return ValueObjectConstResult::Create(nullptr, std::move(error));
@ -567,8 +560,7 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
// Evaluate the parsed expression.
lldb::TargetSP target = this->CalculateTarget();
dil::Interpreter interpreter(target, var_expr, shared_from_this(),
use_dynamic, !no_synth_child, !no_fragile_ivar,
check_ptr_vs_member);
use_dynamic, options);
auto valobj_or_error = interpreter.Evaluate(**tree_or_error);
if (!valobj_or_error) {

View File

@ -388,11 +388,23 @@ lldb::ValueObjectSP LookupIdentifier(llvm::StringRef name_ref,
Interpreter::Interpreter(lldb::TargetSP target, llvm::StringRef expr,
std::shared_ptr<StackFrame> frame_sp,
lldb::DynamicValueType use_dynamic, bool use_synthetic,
bool fragile_ivar, bool check_ptr_vs_member)
lldb::DynamicValueType use_dynamic, uint32_t options)
: m_target(std::move(target)), m_expr(expr), m_exe_ctx_scope(frame_sp),
m_use_dynamic(use_dynamic), m_use_synthetic(use_synthetic),
m_fragile_ivar(fragile_ivar), m_check_ptr_vs_member(check_ptr_vs_member) {
m_use_dynamic(use_dynamic) {
const bool check_ptr_vs_member =
(options & StackFrame::eExpressionPathOptionCheckPtrVsMember) != 0;
const bool no_fragile_ivar =
(options & StackFrame::eExpressionPathOptionsNoFragileObjcIvar) != 0;
const bool no_synth_child =
(options & StackFrame::eExpressionPathOptionsNoSyntheticChildren) != 0;
const bool allow_var_updates =
(options & StackFrame::eExpressionPathOptionsAllowVarUpdates) != 0;
m_use_synthetic = !no_synth_child;
m_fragile_ivar = !no_fragile_ivar;
m_check_ptr_vs_member = check_ptr_vs_member;
m_allow_var_updates = allow_var_updates;
}
llvm::Expected<lldb::ValueObjectSP> Interpreter::Evaluate(const ASTNode &node) {

View File

@ -86,14 +86,23 @@ CompilerType ResolveTypeByName(const std::string &name,
return {};
}
llvm::Expected<ASTNodeUP>
DILParser::Parse(llvm::StringRef dil_input_expr, DILLexer lexer,
std::shared_ptr<StackFrame> frame_sp,
lldb::DynamicValueType use_dynamic, bool use_synthetic,
bool fragile_ivar, bool check_ptr_vs_member) {
llvm::Expected<ASTNodeUP> DILParser::Parse(llvm::StringRef dil_input_expr,
DILLexer lexer,
std::shared_ptr<StackFrame> frame_sp,
lldb::DynamicValueType use_dynamic,
uint32_t options) {
const bool check_ptr_vs_member =
(options & StackFrame::eExpressionPathOptionCheckPtrVsMember) != 0;
const bool no_fragile_ivar =
(options & StackFrame::eExpressionPathOptionsNoFragileObjcIvar) != 0;
const bool no_synth_child =
(options & StackFrame::eExpressionPathOptionsNoSyntheticChildren) != 0;
llvm::Error error = llvm::Error::success();
DILParser parser(dil_input_expr, lexer, frame_sp, use_dynamic, use_synthetic,
fragile_ivar, check_ptr_vs_member, error);
DILParser parser(dil_input_expr, lexer, frame_sp, use_dynamic,
!no_synth_child, !no_fragile_ivar, check_ptr_vs_member,
error);
ASTNodeUP node_up = parser.Run();
assert(node_up && "ASTNodeUP must not contain a nullptr");

View File

@ -1203,22 +1203,23 @@ llvm::Expected<bool> ValueObject::GetValueAsBool() {
return llvm::createStringError("type cannot be converted to bool");
}
void ValueObject::SetValueFromInteger(const llvm::APInt &value, Status &error) {
void ValueObject::SetValueFromInteger(const llvm::APInt &value, Status &error,
bool can_update_var) {
// Verify the current object is an integer object
CompilerType val_type = GetCompilerType();
if (!val_type.IsInteger() && !val_type.IsUnscopedEnumerationType() &&
!HasFloatingRepresentation(val_type) && !val_type.IsPointerType() &&
!val_type.IsScalarType()) {
error =
Status::FromErrorString("current value object is not an integer objet");
Status::FromErrorString("current value object is not an scalar object");
return;
}
// Verify the current object is not actually associated with any program
// variable.
if (GetVariable()) {
// Verify, if current object is associated with a program variable, that
// we are allowing updating program variables in this case.
if (GetVariable() && !can_update_var) {
error = Status::FromErrorString(
"current value object is not a temporary object");
"Not allowed to update program variables in this case.");
return;
}
@ -1242,22 +1243,22 @@ void ValueObject::SetValueFromInteger(const llvm::APInt &value, Status &error) {
}
void ValueObject::SetValueFromInteger(lldb::ValueObjectSP new_val_sp,
Status &error) {
Status &error, bool can_update_var) {
// Verify the current object is an integer object
CompilerType val_type = GetCompilerType();
if (!val_type.IsInteger() && !val_type.IsUnscopedEnumerationType() &&
!HasFloatingRepresentation(val_type) && !val_type.IsPointerType() &&
!val_type.IsScalarType()) {
error =
Status::FromErrorString("current value object is not an integer objet");
Status::FromErrorString("current value object is not an scalar object");
return;
}
// Verify the current object is not actually associated with any program
// variable.
if (GetVariable()) {
// Verify, if current object is associated with a program variable, that
// we are allowing updating program variables in this case.
if (GetVariable() && !can_update_var) {
error = Status::FromErrorString(
"current value object is not a temporary object");
"Not allowed to update program variables in this case.");
return;
}
@ -1273,13 +1274,14 @@ void ValueObject::SetValueFromInteger(lldb::ValueObjectSP new_val_sp,
if (new_val_type.IsInteger()) {
auto value_or_err = new_val_sp->GetValueAsAPSInt();
if (value_or_err)
SetValueFromInteger(*value_or_err, error);
SetValueFromInteger(*value_or_err, error, can_update_var);
else
error = Status::FromErrorString("error getting APSInt from new_val_sp");
} else if (HasFloatingRepresentation(new_val_type)) {
auto value_or_err = new_val_sp->GetValueAsAPFloat();
if (value_or_err)
SetValueFromInteger(value_or_err->bitcastToAPInt(), error);
SetValueFromInteger(value_or_err->bitcastToAPInt(), error,
can_update_var);
else
error = Status::FromErrorString("error getting APFloat from new_val_sp");
} else if (new_val_type.IsPointerType()) {
@ -1291,7 +1293,8 @@ void ValueObject::SetValueFromInteger(lldb::ValueObjectSP new_val_sp,
if (auto temp = llvm::expectedToOptional(
new_val_sp->GetCompilerType().GetBitSize(target.get())))
num_bits = temp.value();
SetValueFromInteger(llvm::APInt(num_bits, int_val), error);
SetValueFromInteger(llvm::APInt(num_bits, int_val), error,
can_update_var);
} else
error = Status::FromErrorString("error converting new_val_sp to integer");
}