[lldb] Improve maintainability and readability for ValueObject methods (#75865)

As I worked through changes to another PR
(https://github.com/llvm/llvm-project/pull/74912), I couldn't help but
rewrite a few methods for readability, maintainability, and possibly
some behavior correctness too.

1. Exiting early instead of nested `if`-statements, which:
	- Reduces indentation levels for all subsequent lines
	- Treats missing pre-conditions similar to an error
- Clearly indicates that the full length of the method is the "happy
path".
2. Explicitly return empty Value Object shared pointers for those error
(like) situations, which
- Reduces the time it takes a maintainer to figure out what the method
actually returns based on those conditions.

3. Converting a mix of `if` and `if`-`else`-statements around an enum
into one `switch` statement, which:
	- Consolidates the former branching logic
	- Lets the compiler warn you of a (future) missing enum case
- This one may actually change behavior slightly, because what was an
early test for one enum case, now happens later on in the `switch`.

4. Consolidating near-identical, "copy-pasta" logic into one place,
which:
	- Separates the common code to the diverging paths.
	- Highlights the differences between the code paths.



rdar://119833526
This commit is contained in:
Pete Lawrence 2024-01-23 14:07:52 -10:00 committed by GitHub
parent 25e1916d88
commit d657519838
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1582,62 +1582,64 @@ bool ValueObject::IsUninitializedReference() {
ValueObjectSP ValueObject::GetSyntheticArrayMember(size_t index, ValueObjectSP ValueObject::GetSyntheticArrayMember(size_t index,
bool can_create) { bool can_create) {
ValueObjectSP synthetic_child_sp; if (!IsPointerType() && !IsArrayType())
if (IsPointerType() || IsArrayType()) { return ValueObjectSP();
std::string index_str = llvm::formatv("[{0}]", index);
ConstString index_const_str(index_str);
// Check if we have already created a synthetic array member in this valid
// object. If we have we will re-use it.
synthetic_child_sp = GetSyntheticChild(index_const_str);
if (!synthetic_child_sp) {
ValueObject *synthetic_child;
// We haven't made a synthetic array member for INDEX yet, so lets make
// one and cache it for any future reference.
synthetic_child = CreateChildAtIndex(0, true, index);
// Cache the value if we got one back... std::string index_str = llvm::formatv("[{0}]", index);
if (synthetic_child) { ConstString index_const_str(index_str);
AddSyntheticChild(index_const_str, synthetic_child); // Check if we have already created a synthetic array member in this valid
synthetic_child_sp = synthetic_child->GetSP(); // object. If we have we will re-use it.
synthetic_child_sp->SetName(ConstString(index_str)); if (auto existing_synthetic_child = GetSyntheticChild(index_const_str))
synthetic_child_sp->m_flags.m_is_array_item_for_pointer = true; return existing_synthetic_child;
}
} // We haven't made a synthetic array member for INDEX yet, so lets make
} // one and cache it for any future reference.
ValueObject *synthetic_child = CreateChildAtIndex(0, true, index);
if (!synthetic_child)
return ValueObjectSP();
// Cache the synthetic child's value because it's valid.
AddSyntheticChild(index_const_str, synthetic_child);
auto synthetic_child_sp = synthetic_child->GetSP();
synthetic_child_sp->SetName(ConstString(index_str));
synthetic_child_sp->m_flags.m_is_array_item_for_pointer = true;
return synthetic_child_sp; return synthetic_child_sp;
} }
ValueObjectSP ValueObject::GetSyntheticBitFieldChild(uint32_t from, uint32_t to, ValueObjectSP ValueObject::GetSyntheticBitFieldChild(uint32_t from, uint32_t to,
bool can_create) { bool can_create) {
ValueObjectSP synthetic_child_sp; if (!IsScalarType())
if (IsScalarType()) { return ValueObjectSP();
std::string index_str = llvm::formatv("[{0}-{1}]", from, to);
ConstString index_const_str(index_str);
// Check if we have already created a synthetic array member in this valid
// object. If we have we will re-use it.
synthetic_child_sp = GetSyntheticChild(index_const_str);
if (!synthetic_child_sp) {
uint32_t bit_field_size = to - from + 1;
uint32_t bit_field_offset = from;
if (GetDataExtractor().GetByteOrder() == eByteOrderBig)
bit_field_offset =
GetByteSize().value_or(0) * 8 - bit_field_size - bit_field_offset;
// We haven't made a synthetic array member for INDEX yet, so lets make
// one and cache it for any future reference.
ValueObjectChild *synthetic_child = new ValueObjectChild(
*this, GetCompilerType(), index_const_str, GetByteSize().value_or(0),
0, bit_field_size, bit_field_offset, false, false,
eAddressTypeInvalid, 0);
// Cache the value if we got one back... std::string index_str = llvm::formatv("[{0}-{1}]", from, to);
if (synthetic_child) { ConstString index_const_str(index_str);
AddSyntheticChild(index_const_str, synthetic_child);
synthetic_child_sp = synthetic_child->GetSP(); // Check if we have already created a synthetic array member in this valid
synthetic_child_sp->SetName(ConstString(index_str)); // object. If we have we will re-use it.
synthetic_child_sp->m_flags.m_is_bitfield_for_scalar = true; if (auto existing_synthetic_child = GetSyntheticChild(index_const_str))
} return existing_synthetic_child;
}
} uint32_t bit_field_size = to - from + 1;
uint32_t bit_field_offset = from;
if (GetDataExtractor().GetByteOrder() == eByteOrderBig)
bit_field_offset =
GetByteSize().value_or(0) * 8 - bit_field_size - bit_field_offset;
// We haven't made a synthetic array member for INDEX yet, so lets make
// one and cache it for any future reference.
ValueObjectChild *synthetic_child = new ValueObjectChild(
*this, GetCompilerType(), index_const_str, GetByteSize().value_or(0), 0,
bit_field_size, bit_field_offset, false, false, eAddressTypeInvalid, 0);
if (!synthetic_child)
return ValueObjectSP();
// Cache the synthetic child's value because it's valid.
AddSyntheticChild(index_const_str, synthetic_child);
auto synthetic_child_sp = synthetic_child->GetSP();
synthetic_child_sp->SetName(ConstString(index_str));
synthetic_child_sp->m_flags.m_is_bitfield_for_scalar = true;
return synthetic_child_sp; return synthetic_child_sp;
} }
@ -1647,9 +1649,8 @@ ValueObjectSP ValueObject::GetSyntheticChildAtOffset(
ValueObjectSP synthetic_child_sp; ValueObjectSP synthetic_child_sp;
if (name_const_str.IsEmpty()) { if (name_const_str.IsEmpty())
name_const_str.SetString("@" + std::to_string(offset)); name_const_str.SetString("@" + std::to_string(offset));
}
// Check if we have already created a synthetic array member in this valid // Check if we have already created a synthetic array member in this valid
// object. If we have we will re-use it. // object. If we have we will re-use it.
@ -1659,13 +1660,13 @@ ValueObjectSP ValueObject::GetSyntheticChildAtOffset(
return synthetic_child_sp; return synthetic_child_sp;
if (!can_create) if (!can_create)
return {}; return ValueObjectSP();
ExecutionContext exe_ctx(GetExecutionContextRef()); ExecutionContext exe_ctx(GetExecutionContextRef());
std::optional<uint64_t> size = std::optional<uint64_t> size =
type.GetByteSize(exe_ctx.GetBestExecutionContextScope()); type.GetByteSize(exe_ctx.GetBestExecutionContextScope());
if (!size) if (!size)
return {}; return ValueObjectSP();
ValueObjectChild *synthetic_child = ValueObjectChild *synthetic_child =
new ValueObjectChild(*this, type, name_const_str, *size, offset, 0, 0, new ValueObjectChild(*this, type, name_const_str, *size, offset, 0, 0,
false, false, eAddressTypeInvalid, 0); false, false, eAddressTypeInvalid, 0);
@ -1699,7 +1700,7 @@ ValueObjectSP ValueObject::GetSyntheticBase(uint32_t offset,
return synthetic_child_sp; return synthetic_child_sp;
if (!can_create) if (!can_create)
return {}; return ValueObjectSP();
const bool is_base_class = true; const bool is_base_class = true;
@ -1707,7 +1708,7 @@ ValueObjectSP ValueObject::GetSyntheticBase(uint32_t offset,
std::optional<uint64_t> size = std::optional<uint64_t> size =
type.GetByteSize(exe_ctx.GetBestExecutionContextScope()); type.GetByteSize(exe_ctx.GetBestExecutionContextScope());
if (!size) if (!size)
return {}; return ValueObjectSP();
ValueObjectChild *synthetic_child = ValueObjectChild *synthetic_child =
new ValueObjectChild(*this, type, name_const_str, *size, offset, 0, 0, new ValueObjectChild(*this, type, name_const_str, *size, offset, 0, 0,
is_base_class, false, eAddressTypeInvalid, 0); is_base_class, false, eAddressTypeInvalid, 0);
@ -1736,30 +1737,30 @@ static const char *SkipLeadingExpressionPathSeparators(const char *expression) {
ValueObjectSP ValueObjectSP
ValueObject::GetSyntheticExpressionPathChild(const char *expression, ValueObject::GetSyntheticExpressionPathChild(const char *expression,
bool can_create) { bool can_create) {
ValueObjectSP synthetic_child_sp;
ConstString name_const_string(expression); ConstString name_const_string(expression);
// Check if we have already created a synthetic array member in this valid // Check if we have already created a synthetic array member in this valid
// object. If we have we will re-use it. // object. If we have we will re-use it.
synthetic_child_sp = GetSyntheticChild(name_const_string); if (auto existing_synthetic_child = GetSyntheticChild(name_const_string))
if (!synthetic_child_sp) { return existing_synthetic_child;
// We haven't made a synthetic array member for expression yet, so lets
// make one and cache it for any future reference.
synthetic_child_sp = GetValueForExpressionPath(
expression, nullptr, nullptr,
GetValueForExpressionPathOptions().SetSyntheticChildrenTraversal(
GetValueForExpressionPathOptions::SyntheticChildrenTraversal::
None));
// Cache the value if we got one back... // We haven't made a synthetic array member for expression yet, so lets
if (synthetic_child_sp.get()) { // make one and cache it for any future reference.
// FIXME: this causes a "real" child to end up with its name changed to auto path_options = GetValueForExpressionPathOptions();
// the contents of expression path_options.SetSyntheticChildrenTraversal(
AddSyntheticChild(name_const_string, synthetic_child_sp.get()); GetValueForExpressionPathOptions::SyntheticChildrenTraversal::None);
synthetic_child_sp->SetName( auto synthetic_child =
ConstString(SkipLeadingExpressionPathSeparators(expression))); GetValueForExpressionPath(expression, nullptr, nullptr, path_options);
}
} if (!synthetic_child)
return synthetic_child_sp; return ValueObjectSP();
// Cache the synthetic child's value because it's valid.
// FIXME: this causes a "real" child to end up with its name changed to
// the contents of expression
AddSyntheticChild(name_const_string, synthetic_child.get());
synthetic_child->SetName(
ConstString(SkipLeadingExpressionPathSeparators(expression)));
return synthetic_child;
} }
void ValueObject::CalculateSyntheticValue() { void ValueObject::CalculateSyntheticValue() {
@ -1956,66 +1957,55 @@ ValueObjectSP ValueObject::GetValueForExpressionPath(
const GetValueForExpressionPathOptions &options, const GetValueForExpressionPathOptions &options,
ExpressionPathAftermath *final_task_on_target) { ExpressionPathAftermath *final_task_on_target) {
ExpressionPathScanEndReason dummy_reason_to_stop = auto dummy_stop_reason = eExpressionPathScanEndReasonUnknown;
ValueObject::eExpressionPathScanEndReasonUnknown; auto dummy_value_type = eExpressionPathEndResultTypeInvalid;
ExpressionPathEndResultType dummy_final_value_type = auto dummy_final_task = eExpressionPathAftermathNothing;
ValueObject::eExpressionPathEndResultTypeInvalid;
ExpressionPathAftermath dummy_final_task_on_target =
ValueObject::eExpressionPathAftermathNothing;
ValueObjectSP ret_val = GetValueForExpressionPath_Impl( auto proxy_stop_reason = reason_to_stop ? reason_to_stop : &dummy_stop_reason;
expression, reason_to_stop ? reason_to_stop : &dummy_reason_to_stop, auto proxy_value_type =
final_value_type ? final_value_type : &dummy_final_value_type, options, final_value_type ? final_value_type : &dummy_value_type;
final_task_on_target ? final_task_on_target auto proxy_final_task =
: &dummy_final_task_on_target); final_task_on_target ? final_task_on_target : &dummy_final_task;
if (!final_task_on_target || auto ret_value = GetValueForExpressionPath_Impl(expression, proxy_stop_reason,
*final_task_on_target == ValueObject::eExpressionPathAftermathNothing) proxy_value_type, options,
return ret_val; proxy_final_task);
if (ret_val.get() && // The caller knows nothing happened if `final_task_on_target` doesn't change.
((final_value_type ? *final_value_type : dummy_final_value_type) == if (!ret_value || (*proxy_value_type) != eExpressionPathEndResultTypePlain ||
eExpressionPathEndResultTypePlain)) // I can only deref and takeaddress !final_task_on_target)
// of plain objects return ValueObjectSP();
{
if ((final_task_on_target ? *final_task_on_target ExpressionPathAftermath &final_task_on_target_ref = (*final_task_on_target);
: dummy_final_task_on_target) == ExpressionPathScanEndReason stop_reason_for_error;
ValueObject::eExpressionPathAftermathDereference) { Status error;
Status error; // The method can only dereference and take the address of plain objects.
ValueObjectSP final_value = ret_val->Dereference(error); switch (final_task_on_target_ref) {
if (error.Fail() || !final_value.get()) { case eExpressionPathAftermathNothing:
if (reason_to_stop) return ret_value;
*reason_to_stop =
ValueObject::eExpressionPathScanEndReasonDereferencingFailed; case eExpressionPathAftermathDereference:
if (final_value_type) ret_value = ret_value->Dereference(error);
*final_value_type = ValueObject::eExpressionPathEndResultTypeInvalid; stop_reason_for_error = eExpressionPathScanEndReasonDereferencingFailed;
return ValueObjectSP(); break;
} else {
if (final_task_on_target) case eExpressionPathAftermathTakeAddress:
*final_task_on_target = ValueObject::eExpressionPathAftermathNothing; ret_value = ret_value->AddressOf(error);
return final_value; stop_reason_for_error = eExpressionPathScanEndReasonTakingAddressFailed;
} break;
}
if (*final_task_on_target ==
ValueObject::eExpressionPathAftermathTakeAddress) {
Status error;
ValueObjectSP final_value = ret_val->AddressOf(error);
if (error.Fail() || !final_value.get()) {
if (reason_to_stop)
*reason_to_stop =
ValueObject::eExpressionPathScanEndReasonTakingAddressFailed;
if (final_value_type)
*final_value_type = ValueObject::eExpressionPathEndResultTypeInvalid;
return ValueObjectSP();
} else {
if (final_task_on_target)
*final_task_on_target = ValueObject::eExpressionPathAftermathNothing;
return final_value;
}
}
} }
return ret_val; // final_task_on_target will still have its original value, so
// you know I did not do it if (ret_value && error.Success()) {
final_task_on_target_ref = eExpressionPathAftermathNothing;
return ret_value;
}
if (reason_to_stop)
*reason_to_stop = stop_reason_for_error;
if (final_value_type)
*final_value_type = eExpressionPathEndResultTypeInvalid;
return ValueObjectSP();
} }
ValueObjectSP ValueObject::GetValueForExpressionPath_Impl( ValueObjectSP ValueObject::GetValueForExpressionPath_Impl(
@ -2686,39 +2676,47 @@ ValueObjectSP ValueObject::AddressOf(Status &error) {
const bool scalar_is_load_address = false; const bool scalar_is_load_address = false;
addr_t addr = GetAddressOf(scalar_is_load_address, &address_type); addr_t addr = GetAddressOf(scalar_is_load_address, &address_type);
error.Clear(); error.Clear();
if (addr != LLDB_INVALID_ADDRESS && address_type != eAddressTypeHost) {
switch (address_type) {
case eAddressTypeInvalid: {
StreamString expr_path_strm;
GetExpressionPath(expr_path_strm);
error.SetErrorStringWithFormat("'%s' is not in memory",
expr_path_strm.GetData());
} break;
case eAddressTypeFile: StreamString expr_path_strm;
case eAddressTypeLoad: { GetExpressionPath(expr_path_strm);
CompilerType compiler_type = GetCompilerType(); const char *expr_path_str = expr_path_strm.GetData();
if (compiler_type) {
std::string name(1, '&'); ExecutionContext exe_ctx(GetExecutionContextRef());
name.append(m_name.AsCString("")); auto scope = exe_ctx.GetBestExecutionContextScope();
ExecutionContext exe_ctx(GetExecutionContextRef());
m_addr_of_valobj_sp = ValueObjectConstResult::Create( if (addr == LLDB_INVALID_ADDRESS) {
exe_ctx.GetBestExecutionContextScope(),
compiler_type.GetPointerType(), ConstString(name.c_str()), addr,
eAddressTypeInvalid, m_data.GetAddressByteSize());
}
} break;
default:
break;
}
} else {
StreamString expr_path_strm;
GetExpressionPath(expr_path_strm);
error.SetErrorStringWithFormat("'%s' doesn't have a valid address", error.SetErrorStringWithFormat("'%s' doesn't have a valid address",
expr_path_strm.GetData()); expr_path_str);
return ValueObjectSP();
} }
return m_addr_of_valobj_sp; switch (address_type) {
case eAddressTypeInvalid:
error.SetErrorStringWithFormat("'%s' is not in memory", expr_path_str);
return ValueObjectSP();
case eAddressTypeHost:
error.SetErrorStringWithFormat("'%s' is in host process (LLDB) memory",
expr_path_str);
return ValueObjectSP();
case eAddressTypeFile:
case eAddressTypeLoad: {
CompilerType compiler_type = GetCompilerType();
if (!compiler_type) {
error.SetErrorStringWithFormat("'%s' doesn't have a compiler type",
expr_path_str);
return ValueObjectSP();
}
std::string name(1, '&');
name.append(m_name.AsCString(""));
m_addr_of_valobj_sp = ValueObjectConstResult::Create(
scope, compiler_type.GetPointerType(), ConstString(name.c_str()), addr,
eAddressTypeInvalid, m_data.GetAddressByteSize());
return m_addr_of_valobj_sp;
}
}
} }
ValueObjectSP ValueObject::DoCast(const CompilerType &compiler_type) { ValueObjectSP ValueObject::DoCast(const CompilerType &compiler_type) {