Revert "Make result variables obey their dynamic values in subsequent expressions" (#211321)
This reverts commit 8b9cce358b.
This causes failures running expressions that return ObjC types on
x86-64 macOS but not on arm64 macOS.
Reverting till I can figure out why that's happening.
This commit is contained in:
parent
0e9b8c3dfa
commit
e1af868257
10 changed files with 56 additions and 384 deletions
|
|
@ -33,19 +33,11 @@ public:
|
|||
|
||||
virtual ~ExpressionVariable() = default;
|
||||
|
||||
llvm::Expected<uint64_t> GetByteSize() {
|
||||
return GetValueObject()->GetByteSize();
|
||||
}
|
||||
llvm::Expected<uint64_t> GetByteSize() { return m_frozen_sp->GetByteSize(); }
|
||||
|
||||
ConstString GetName() { return m_frozen_sp->GetName(); }
|
||||
|
||||
lldb::ValueObjectSP GetValueObject() {
|
||||
lldb::ValueObjectSP dyn_sp =
|
||||
m_frozen_sp->GetDynamicValue(lldb::eDynamicDontRunTarget);
|
||||
if (dyn_sp && dyn_sp->UpdateValueIfNeeded())
|
||||
return dyn_sp;
|
||||
return m_frozen_sp;
|
||||
}
|
||||
lldb::ValueObjectSP GetValueObject() { return m_frozen_sp; }
|
||||
|
||||
uint8_t *GetValueBytes();
|
||||
|
||||
|
|
@ -60,7 +52,7 @@ public:
|
|||
Value::ContextType::RegisterInfo, const_cast<RegisterInfo *>(reg_info));
|
||||
}
|
||||
|
||||
CompilerType GetCompilerType() { return GetValueObject()->GetCompilerType(); }
|
||||
CompilerType GetCompilerType() { return m_frozen_sp->GetCompilerType(); }
|
||||
|
||||
void SetCompilerType(const CompilerType &compiler_type) {
|
||||
m_frozen_sp->GetValue().SetCompilerType(compiler_type);
|
||||
|
|
@ -68,31 +60,23 @@ public:
|
|||
|
||||
void SetName(llvm::StringRef name) { m_frozen_sp->SetName(name); }
|
||||
|
||||
/// This function is used to copy the address-of m_live_sp into m_frozen_sp.
|
||||
/// It is necessary because the results of certain cast and pointer-
|
||||
/// arithmetic operations (such as those described in bugzilla issues 11588
|
||||
/// and 11618) generate frozen objects that do not have a valid address-of,
|
||||
/// which can be troublesome when using synthetic children providers.
|
||||
/// Transferring the address-of the live object solves these issues and
|
||||
/// provides the expected user-level behavior.
|
||||
/// The other job we do in TransferAddress is adjust the value in the live
|
||||
/// address slot in the target for the "offset to top" in multiply inherited
|
||||
/// class hierarchies.
|
||||
void TransferAddress(bool force = false);
|
||||
// this function is used to copy the address-of m_live_sp into m_frozen_sp
|
||||
// this is necessary because the results of certain cast and pointer-
|
||||
// arithmetic operations (such as those described in bugzilla issues 11588
|
||||
// and 11618) generate frozen objects that do not have a valid address-of,
|
||||
// which can be troublesome when using synthetic children providers.
|
||||
// Transferring the address-of the live object solves these issues and
|
||||
// provides the expected user-level behavior
|
||||
void TransferAddress(bool force = false) {
|
||||
if (m_live_sp.get() == nullptr)
|
||||
return;
|
||||
|
||||
/// When we build an expression variable we know whether we're going to use
|
||||
/// the static or dynamic result. If we present the dynamic value once, we
|
||||
/// should use the dynamic value in future references to the variable, so we
|
||||
/// record that fact here.
|
||||
void PreserveDynamicOption(lldb::DynamicValueType dyn_type) {
|
||||
m_dyn_option = dyn_type;
|
||||
if (m_frozen_sp.get() == nullptr)
|
||||
return;
|
||||
|
||||
if (force || (m_frozen_sp->GetLiveAddress() == LLDB_INVALID_ADDRESS))
|
||||
m_frozen_sp->SetLiveAddress(m_live_sp->GetLiveAddress());
|
||||
}
|
||||
/// We don't try to get the dynamic value of the live object when we fetch
|
||||
/// it here. The live object describes the container of the value in the
|
||||
/// target, but it's type is of the object for convenience. So it can't
|
||||
/// produce the dynamic value. Instead, we use TransferAddress to adjust the
|
||||
/// value held by the LiveObject.
|
||||
lldb::ValueObjectSP GetLiveObject() { return m_live_sp; }
|
||||
|
||||
enum Flags {
|
||||
EVNone = 0,
|
||||
|
|
@ -126,14 +110,6 @@ public:
|
|||
/// These members should be private.
|
||||
/// @{
|
||||
/// A value object whose value's data lives in host (lldb's) memory.
|
||||
/// The m_frozen_sp holds the data & type of the expression variable or result
|
||||
/// in the host. The m_frozen_sp also can present a dynamic value if one is
|
||||
/// available.
|
||||
/// The m_frozen_sp manages the copy of this value in m_frozen_sp that we
|
||||
/// insert in the target so that it can be referred to in future expressions.
|
||||
/// We don't actually use the contents of the live_sp to create the value in
|
||||
/// the target, that comes from the frozen sp. The live_sp is mostly to track
|
||||
/// the target-side of the value.
|
||||
lldb::ValueObjectSP m_frozen_sp;
|
||||
/// The ValueObject counterpart to m_frozen_sp that tracks the value in
|
||||
/// inferior memory. This object may not always exist; its presence depends on
|
||||
|
|
@ -143,8 +119,6 @@ public:
|
|||
/// track.
|
||||
lldb::ValueObjectSP m_live_sp;
|
||||
/// @}
|
||||
|
||||
lldb::DynamicValueType m_dyn_option = lldb::eNoDynamicValues;
|
||||
};
|
||||
|
||||
/// \class ExpressionVariableList ExpressionVariable.h
|
||||
|
|
|
|||
|
|
@ -2967,6 +2967,7 @@ FileCheck output:
|
|||
)
|
||||
|
||||
frame = self.frame()
|
||||
|
||||
if not options:
|
||||
options = lldb.SBExpressionOptions()
|
||||
|
||||
|
|
|
|||
|
|
@ -20,15 +20,15 @@ char ExpressionVariable::ID;
|
|||
ExpressionVariable::ExpressionVariable() : m_flags(0) {}
|
||||
|
||||
uint8_t *ExpressionVariable::GetValueBytes() {
|
||||
lldb::ValueObjectSP valobj_sp = GetValueObject();
|
||||
std::optional<uint64_t> byte_size =
|
||||
llvm::expectedToOptional(valobj_sp->GetByteSize());
|
||||
llvm::expectedToOptional(m_frozen_sp->GetByteSize());
|
||||
if (byte_size && *byte_size) {
|
||||
if (valobj_sp->GetDataExtractor().GetByteSize() < *byte_size) {
|
||||
valobj_sp->GetValue().ResizeData(*byte_size);
|
||||
valobj_sp->GetValue().GetData(valobj_sp->GetDataExtractor());
|
||||
if (m_frozen_sp->GetDataExtractor().GetByteSize() < *byte_size) {
|
||||
m_frozen_sp->GetValue().ResizeData(*byte_size);
|
||||
m_frozen_sp->GetValue().GetData(m_frozen_sp->GetDataExtractor());
|
||||
}
|
||||
return const_cast<uint8_t *>(valobj_sp->GetDataExtractor().GetDataStart());
|
||||
return const_cast<uint8_t *>(
|
||||
m_frozen_sp->GetDataExtractor().GetDataStart());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -37,72 +37,6 @@ char PersistentExpressionState::ID;
|
|||
|
||||
PersistentExpressionState::PersistentExpressionState() = default;
|
||||
|
||||
void ExpressionVariable::TransferAddress(bool force) {
|
||||
if (!m_live_sp)
|
||||
return;
|
||||
|
||||
if (!m_frozen_sp)
|
||||
return;
|
||||
|
||||
if (force || (m_frozen_sp->GetLiveAddress() == LLDB_INVALID_ADDRESS)) {
|
||||
lldb::addr_t live_addr = m_live_sp->GetLiveAddress();
|
||||
m_frozen_sp->SetLiveAddress(live_addr);
|
||||
// One more detail, if there's an offset_to_top in the frozen_sp, then we
|
||||
// need to appy that offset by hand. The live_sp can't compute this
|
||||
// itself as its type is the type of the contained object which confuses
|
||||
// the dynamic type calculation. So we have to update the contents of the
|
||||
// m_live_sp with the dynamic value.
|
||||
// Note: We could get this right when we originally write the address, but
|
||||
// that happens in different ways for the various flavors of
|
||||
// Entity*::Materialize, but everything comes through here, and it's just
|
||||
// one extra memory write.
|
||||
|
||||
// You can only have an "offset_to_top" with pointers or references:
|
||||
if (!m_frozen_sp->GetCompilerType().IsPointerOrReferenceType())
|
||||
return;
|
||||
|
||||
lldb::ProcessSP process_sp = m_frozen_sp->GetProcessSP();
|
||||
// If there's no dynamic value, then there can't be an offset_to_top:
|
||||
if (!process_sp ||
|
||||
!process_sp->IsPossibleDynamicValue(*(m_frozen_sp.get())))
|
||||
return;
|
||||
|
||||
lldb::ValueObjectSP dyn_sp = m_frozen_sp->GetDynamicValue(m_dyn_option);
|
||||
if (!dyn_sp)
|
||||
return;
|
||||
ValueObject::AddrAndType static_addr = m_frozen_sp->GetPointerValue();
|
||||
if (static_addr.type != eAddressTypeLoad)
|
||||
return;
|
||||
|
||||
ValueObject::AddrAndType dynamic_addr = dyn_sp->GetPointerValue();
|
||||
if (dynamic_addr.type != eAddressTypeLoad ||
|
||||
static_addr.address == dynamic_addr.address)
|
||||
return;
|
||||
|
||||
Status error;
|
||||
Log *log = GetLog(LLDBLog::Expressions);
|
||||
lldb::addr_t cur_value =
|
||||
process_sp->ReadPointerFromMemory(live_addr, error);
|
||||
if (error.Fail())
|
||||
return;
|
||||
|
||||
if (cur_value != static_addr.address) {
|
||||
LLDB_LOG(log,
|
||||
"Stored value: {0} read from {1} doesn't "
|
||||
"match static addr: {2}",
|
||||
cur_value, live_addr, static_addr.address);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!process_sp->WritePointerToMemory(live_addr, dynamic_addr.address,
|
||||
error)) {
|
||||
LLDB_LOG(log, "Got error: {0} writing dynamic value: {1} to {2}", error,
|
||||
dynamic_addr.address, live_addr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PersistentExpressionState::~PersistentExpressionState() = default;
|
||||
|
||||
lldb::addr_t PersistentExpressionState::LookupSymbol(ConstString name) {
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ LLVMUserExpression::DoExecute(DiagnosticManager &diagnostic_manager,
|
|||
ExecutionContext &exe_ctx,
|
||||
const EvaluateExpressionOptions &options,
|
||||
lldb::UserExpressionSP &shared_ptr_to_me,
|
||||
lldb::ExpressionVariableSP &result_sp) {
|
||||
lldb::ExpressionVariableSP &result) {
|
||||
// The expression log is quite verbose, and if you're just tracking the
|
||||
// execution of the expression, it's quite convenient to have these logs come
|
||||
// out with the STEP log as well.
|
||||
|
|
@ -254,9 +254,10 @@ LLVMUserExpression::DoExecute(DiagnosticManager &diagnostic_manager,
|
|||
}
|
||||
}
|
||||
|
||||
if (FinalizeJITExecution(diagnostic_manager, exe_ctx, result_sp,
|
||||
function_stack_bottom, function_stack_top))
|
||||
if (FinalizeJITExecution(diagnostic_manager, exe_ctx, result,
|
||||
function_stack_bottom, function_stack_top)) {
|
||||
return lldb::eExpressionCompleted;
|
||||
}
|
||||
|
||||
return lldb::eExpressionResultUnavailable;
|
||||
}
|
||||
|
|
@ -292,13 +293,8 @@ bool LLVMUserExpression::FinalizeJITExecution(
|
|||
result =
|
||||
GetResultAfterDematerialization(exe_ctx.GetBestExecutionContextScope());
|
||||
|
||||
if (result) {
|
||||
// TransferAddress also does the offset_to_top calculation, so record the
|
||||
// dynamic option before we do that.
|
||||
if (EvaluateExpressionOptions *options = GetOptions())
|
||||
result->PreserveDynamicOption(options->GetUseDynamic());
|
||||
if (result)
|
||||
result->TransferAddress();
|
||||
}
|
||||
|
||||
m_dematerializer_sp.reset();
|
||||
|
||||
|
|
|
|||
|
|
@ -76,11 +76,10 @@ public:
|
|||
|
||||
const bool zero_memory = false;
|
||||
IRMemoryMap::AllocationPolicy used_policy;
|
||||
const uint64_t malloc_size =
|
||||
llvm::expectedToOptional(m_persistent_variable_sp->GetByteSize())
|
||||
.value_or(0);
|
||||
auto address_or_error = map.Malloc(
|
||||
malloc_size, 8, lldb::ePermissionsReadable | lldb::ePermissionsWritable,
|
||||
llvm::expectedToOptional(m_persistent_variable_sp->GetByteSize())
|
||||
.value_or(0),
|
||||
8, lldb::ePermissionsReadable | lldb::ePermissionsWritable,
|
||||
IRMemoryMap::eAllocationPolicyMirror, zero_memory, &used_policy);
|
||||
if (!address_or_error) {
|
||||
err = Status::FromErrorStringWithFormat(
|
||||
|
|
@ -91,9 +90,8 @@ public:
|
|||
}
|
||||
lldb::addr_t mem = *address_or_error;
|
||||
|
||||
LLDB_LOGF(
|
||||
log, "Allocated 0x%" PRIx64 "bytes for %s (0x%" PRIx64 ") successfully",
|
||||
malloc_size, m_persistent_variable_sp->GetName().GetCString(), mem);
|
||||
LLDB_LOGF(log, "Allocated %s (0x%" PRIx64 ") successfully",
|
||||
m_persistent_variable_sp->GetName().GetCString(), mem);
|
||||
|
||||
// Put the location of the spare memory into the live data of the
|
||||
// ValueObject.
|
||||
|
|
@ -144,12 +142,12 @@ public:
|
|||
void DestroyAllocation(IRMemoryMap &map, Status &err) {
|
||||
Status deallocate_error;
|
||||
|
||||
lldb::ValueObjectSP live_valobj_sp =
|
||||
m_persistent_variable_sp->GetLiveObject();
|
||||
map.Free((lldb::addr_t)live_valobj_sp->GetValue().GetScalar().ULongLong(),
|
||||
map.Free((lldb::addr_t)m_persistent_variable_sp->m_live_sp->GetValue()
|
||||
.GetScalar()
|
||||
.ULongLong(),
|
||||
deallocate_error);
|
||||
|
||||
live_valobj_sp.reset();
|
||||
m_persistent_variable_sp->m_live_sp.reset();
|
||||
|
||||
if (!deallocate_error.Success()) {
|
||||
err = Status::FromErrorStringWithFormat(
|
||||
|
|
@ -181,17 +179,17 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
lldb::ValueObjectSP live_valobj_sp =
|
||||
m_persistent_variable_sp->GetLiveObject();
|
||||
if ((m_persistent_variable_sp->m_flags &
|
||||
ExpressionVariable::EVIsProgramReference &&
|
||||
live_valobj_sp) ||
|
||||
m_persistent_variable_sp->m_live_sp) ||
|
||||
m_persistent_variable_sp->m_flags &
|
||||
ExpressionVariable::EVIsLLDBAllocated) {
|
||||
Status write_error;
|
||||
|
||||
map.WriteScalarToMemory(load_addr, live_valobj_sp->GetValue().GetScalar(),
|
||||
map.GetAddressByteSize(), write_error);
|
||||
map.WriteScalarToMemory(
|
||||
load_addr,
|
||||
m_persistent_variable_sp->m_live_sp->GetValue().GetScalar(),
|
||||
map.GetAddressByteSize(), write_error);
|
||||
|
||||
if (!write_error.Success()) {
|
||||
err = Status::FromErrorStringWithFormatv(
|
||||
|
|
@ -224,15 +222,13 @@ public:
|
|||
m_delegate->DidDematerialize(m_persistent_variable_sp);
|
||||
}
|
||||
|
||||
lldb::ValueObjectSP live_valobj_sp =
|
||||
m_persistent_variable_sp->GetLiveObject();
|
||||
if ((m_persistent_variable_sp->m_flags &
|
||||
ExpressionVariable::EVIsLLDBAllocated) ||
|
||||
(m_persistent_variable_sp->m_flags &
|
||||
ExpressionVariable::EVIsProgramReference)) {
|
||||
if (m_persistent_variable_sp->m_flags &
|
||||
ExpressionVariable::EVIsProgramReference &&
|
||||
!live_valobj_sp) {
|
||||
!m_persistent_variable_sp->m_live_sp) {
|
||||
// If the reference comes from the program, then the
|
||||
// ClangExpressionVariable's live variable data hasn't been set up yet.
|
||||
// Do this now.
|
||||
|
|
@ -252,7 +248,7 @@ public:
|
|||
|
||||
m_persistent_variable_sp->m_live_sp = ValueObjectConstResult::Create(
|
||||
map.GetBestExecutionContextScope(),
|
||||
m_persistent_variable_sp->GetCompilerType(),
|
||||
m_persistent_variable_sp.get()->GetCompilerType(),
|
||||
m_persistent_variable_sp->GetName(), location, eAddressTypeLoad,
|
||||
llvm::expectedToOptional(m_persistent_variable_sp->GetByteSize())
|
||||
.value_or(0));
|
||||
|
|
@ -274,17 +270,19 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
if (!live_valobj_sp) {
|
||||
lldb::addr_t mem = m_persistent_variable_sp->m_live_sp->GetValue()
|
||||
.GetScalar()
|
||||
.ULongLong();
|
||||
|
||||
if (!m_persistent_variable_sp->m_live_sp) {
|
||||
err = Status::FromErrorStringWithFormat(
|
||||
"couldn't find the memory area used to store %s",
|
||||
m_persistent_variable_sp->GetName().GetCString());
|
||||
return;
|
||||
}
|
||||
|
||||
lldb::addr_t mem = live_valobj_sp->GetValue().GetScalar().ULongLong();
|
||||
|
||||
if (live_valobj_sp->GetValue().GetValueAddressType() !=
|
||||
eAddressTypeLoad) {
|
||||
if (m_persistent_variable_sp->m_live_sp->GetValue()
|
||||
.GetValueAddressType() != eAddressTypeLoad) {
|
||||
err = Status::FromErrorStringWithFormat(
|
||||
"the address of the memory area for %s is in an incorrect format",
|
||||
m_persistent_variable_sp->GetName().GetCString());
|
||||
|
|
@ -321,6 +319,7 @@ public:
|
|||
read_error.AsCString());
|
||||
return;
|
||||
}
|
||||
|
||||
m_persistent_variable_sp->m_flags &=
|
||||
~ExpressionVariable::EVNeedsFreezeDry;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,6 @@ ClangExpressionVariable::ClangExpressionVariable(
|
|||
}
|
||||
|
||||
TypeFromUser ClangExpressionVariable::GetTypeFromUser() {
|
||||
TypeFromUser tfu(GetValueObject()->GetCompilerType());
|
||||
TypeFromUser tfu(m_frozen_sp->GetCompilerType());
|
||||
return tfu;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ ValueObjectSP ABI::GetReturnValueObject(Thread &thread, CompilerType &ast_type,
|
|||
ExpressionVariable::EVNeedsAllocation;
|
||||
break;
|
||||
case Value::ValueType::LoadAddress:
|
||||
expr_variable_sp->GetLiveObject() = live_valobj_sp;
|
||||
expr_variable_sp->m_live_sp = live_valobj_sp;
|
||||
expr_variable_sp->m_flags |=
|
||||
ExpressionVariable::EVIsProgramReference;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
CXX_SOURCES := two-bases.cpp
|
||||
|
||||
include Makefile.rules
|
||||
|
|
@ -1,174 +0,0 @@
|
|||
"""
|
||||
Test the reuse of C++ result variables, particularly making sure
|
||||
that the dynamic typing is preserved.
|
||||
"""
|
||||
|
||||
|
||||
import lldb
|
||||
from lldbsuite.test.decorators import *
|
||||
from lldbsuite.test.lldbtest import *
|
||||
from lldbsuite.test import lldbutil
|
||||
|
||||
|
||||
class TestCPPResultVariables(TestBase):
|
||||
NO_DEBUG_INFO_TESTCASE = True
|
||||
SHARED_BUILD_TESTCASE = False
|
||||
|
||||
def setUp(self):
|
||||
TestBase.setUp(self)
|
||||
self.main_source_file = lldb.SBFileSpec("two-bases.cpp")
|
||||
|
||||
def check_dereference(self, result_varname, frame, expr_options):
|
||||
deref_expr = "*{0}".format(result_varname)
|
||||
base_children = ValueCheck(
|
||||
name="Base", value="", children=[ValueCheck(name="base_int", value="100")]
|
||||
)
|
||||
base_1_arr_children = [
|
||||
ValueCheck(name="[0]", value="100"),
|
||||
ValueCheck(name="[1]", value="101"),
|
||||
ValueCheck(name="[2]", value="102"),
|
||||
ValueCheck(name="[3]", value="103"),
|
||||
ValueCheck(name="[4]", value="104"),
|
||||
ValueCheck(name="[5]", value="105"),
|
||||
ValueCheck(name="[6]", value="106"),
|
||||
ValueCheck(name="[7]", value="107"),
|
||||
ValueCheck(name="[8]", value="108"),
|
||||
ValueCheck(name="[9]", value="109"),
|
||||
]
|
||||
base_2_arr_children = [
|
||||
ValueCheck(name="[0]", value="200"),
|
||||
ValueCheck(name="[1]", value="201"),
|
||||
ValueCheck(name="[2]", value="202"),
|
||||
ValueCheck(name="[3]", value="203"),
|
||||
ValueCheck(name="[4]", value="204"),
|
||||
ValueCheck(name="[5]", value="205"),
|
||||
ValueCheck(name="[6]", value="206"),
|
||||
ValueCheck(name="[7]", value="207"),
|
||||
ValueCheck(name="[8]", value="208"),
|
||||
ValueCheck(name="[9]", value="209"),
|
||||
]
|
||||
deref_children = [
|
||||
ValueCheck(
|
||||
name="Base_1",
|
||||
value="",
|
||||
children=[
|
||||
base_children,
|
||||
ValueCheck(
|
||||
name="base_1_arr", value="", children=base_1_arr_children
|
||||
),
|
||||
],
|
||||
),
|
||||
ValueCheck(
|
||||
name="Base_2",
|
||||
value="",
|
||||
children=[
|
||||
base_children,
|
||||
ValueCheck(
|
||||
name="base_2_arr", value="", children=base_2_arr_children
|
||||
),
|
||||
],
|
||||
),
|
||||
ValueCheck(name="derived_int", value="1000"),
|
||||
]
|
||||
result_var_deref = self.expect_expr(
|
||||
deref_expr,
|
||||
result_type="Derived",
|
||||
result_children=deref_children,
|
||||
options=expr_options,
|
||||
)
|
||||
|
||||
direct_access_expr = "{0}->derived_int".format(result_varname)
|
||||
self.expect_expr(direct_access_expr, result_type="int", result_value="1000")
|
||||
|
||||
# Also check this by directly accessing the result variable:
|
||||
result_value = frame.FindValue(result_varname, lldb.eValueTypeConstResult, True)
|
||||
self.assertTrue(result_value.error.success, "Found my result variable")
|
||||
value_check = ValueCheck(children=deref_children)
|
||||
value_check.check_value(
|
||||
self, result_value, f"{result_varname} children are correct"
|
||||
)
|
||||
|
||||
# Make sure we can also call a function through the derived type:
|
||||
method_result = self.expect_expr(
|
||||
f"{result_varname}->method_of_derived()",
|
||||
result_type="int",
|
||||
options=expr_options,
|
||||
)
|
||||
self.assertEqual(method_result.signed, 500, "Got the right result value")
|
||||
|
||||
@expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24663")
|
||||
def test_virtual_dynamic_results(self):
|
||||
self.do_test_dynamic_results(True)
|
||||
|
||||
@expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24663")
|
||||
def test_non_virtual_dynamic_results(self):
|
||||
self.do_test_dynamic_results(False)
|
||||
|
||||
def do_test_dynamic_results(self, virtual):
|
||||
"""Test that when we uses a result variable in a subsequent expression it
|
||||
uses the dynamic value - if that was requested when the result variable was made.
|
||||
"""
|
||||
if virtual:
|
||||
self.build(dictionary={"CFLAGS_EXTRAS": "-DVIRTUAL=''"})
|
||||
else:
|
||||
self.build(dictionary={"CFLAGS_EXTRAS": "-DVIRTUAL='virtual'"})
|
||||
|
||||
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
|
||||
self, "Set a breakpoint here", self.main_source_file
|
||||
)
|
||||
|
||||
frame = thread.GetFrameAtIndex(0)
|
||||
expr_options = lldb.SBExpressionOptions()
|
||||
expr_options.SetFetchDynamicValue(lldb.eDynamicDontRunTarget)
|
||||
base_1_ptr = self.expect_expr(
|
||||
"base_1_ptr", result_type="Derived *", options=expr_options
|
||||
)
|
||||
result_varname = base_1_ptr.GetName()
|
||||
self.check_dereference(result_varname, frame, expr_options)
|
||||
|
||||
# Now do the same thing, but use a persistent result variable:
|
||||
empty_var = frame.EvaluateExpression(
|
||||
"void *$base_1_ptr = base_1_ptr", expr_options
|
||||
)
|
||||
self.assertIn(
|
||||
empty_var.error.description,
|
||||
"unknown error",
|
||||
"Expressions that don't have results return this error",
|
||||
)
|
||||
persist_base_1_ptr = frame.FindValue(
|
||||
"$base_1_ptr", lldb.eValueTypeConstResult, True
|
||||
)
|
||||
self.assertTrue(persist_base_1_ptr.error.success, "Got the persistent variable")
|
||||
self.check_dereference("$base_1_ptr", frame, expr_options)
|
||||
|
||||
# Now check the second of the multiply inherited bases, this one will have an offset_to_top
|
||||
# that we need to calculate:
|
||||
base_2_ptr = self.expect_expr(
|
||||
"base_2_ptr", result_type="Derived *", options=expr_options
|
||||
)
|
||||
self.check_dereference(base_2_ptr.GetName(), frame, expr_options)
|
||||
|
||||
# Again, do the same thing for a persistent expression variable:
|
||||
empty_var = frame.EvaluateExpression(
|
||||
"void *$base_2_ptr = base_2_ptr", expr_options
|
||||
)
|
||||
self.check_dereference("$base_2_ptr", frame, expr_options)
|
||||
|
||||
# Now try starting from a virtual base class of both our bases:
|
||||
base_through_1 = self.expect_expr(
|
||||
"base_through_1", result_type="Derived *", options=expr_options
|
||||
)
|
||||
self.check_dereference(base_through_1.GetName(), frame, expr_options)
|
||||
|
||||
# Now try starting from a virtual base class of both our bases:
|
||||
base_through_2 = self.expect_expr(
|
||||
"base_through_2", result_type="Derived *", options=expr_options
|
||||
)
|
||||
self.check_dereference(base_through_2.GetName(), frame, expr_options)
|
||||
|
||||
# Now check that we get the right results when we run an
|
||||
# expression to get the base class object:
|
||||
base_through_expr = self.expect_expr(
|
||||
"MakeADerivedReportABase()", result_type="Derived *", options=expr_options
|
||||
)
|
||||
self.check_dereference(base_through_expr.GetName(), frame, expr_options)
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct Base {
|
||||
virtual ~Base() = default;
|
||||
int base_int = 100;
|
||||
Base *return_me() { return this; }
|
||||
};
|
||||
|
||||
struct Base_1 : public VIRTUAL Base {
|
||||
virtual ~Base_1() = default;
|
||||
int base_1_arr[10] = {100, 101, 102, 103, 104, 105, 106, 107, 108, 109};
|
||||
Base *return_base_1() { return return_me(); }
|
||||
};
|
||||
|
||||
struct Base_2 : public VIRTUAL Base {
|
||||
virtual ~Base_2() = default;
|
||||
int base_2_arr[10] = {200, 201, 202, 203, 204, 205, 206, 207, 208, 209};
|
||||
Base *return_base_2() { return return_me(); }
|
||||
};
|
||||
|
||||
struct Derived : public Base_1, Base_2 {
|
||||
virtual ~Derived() = default;
|
||||
int derived_int = 1000;
|
||||
int method_of_derived() { return 500; }
|
||||
};
|
||||
|
||||
Base *MakeADerivedReportABase() { return (Base *)((Base_1 *)new Derived()); }
|
||||
|
||||
int main() {
|
||||
Derived my_derived;
|
||||
int call_it = my_derived.method_of_derived();
|
||||
|
||||
Base_1 *base_1_ptr = (Base_1 *)&my_derived;
|
||||
|
||||
Base_2 *base_2_ptr = (Base_2 *)&my_derived;
|
||||
|
||||
Base *base_through_1 = my_derived.return_base_1();
|
||||
Base *base_through_2 = my_derived.return_base_2();
|
||||
|
||||
// Call this to make sure the compiler makes it.
|
||||
Base *fake_base = MakeADerivedReportABase();
|
||||
|
||||
uint64_t base_through_1_addr = (uint64_t)base_through_1;
|
||||
uint64_t base_through_2_addr = (uint64_t)base_through_2;
|
||||
int64_t base_offset = base_through_2_addr - base_through_1_addr;
|
||||
printf("Base offset (should be 0): 0x%llx.\n", base_offset);
|
||||
uint64_t base_1_addr = (uint64_t)base_1_ptr;
|
||||
uint64_t base_2_addr = (uint64_t)base_2_ptr;
|
||||
int64_t offset = base_2_addr - base_1_addr;
|
||||
|
||||
// Set a breakpoint here
|
||||
return my_derived.derived_int + base_1_ptr->base_1_arr[0] +
|
||||
base_2_ptr->base_2_arr[0] + my_derived.return_base_1()->base_int;
|
||||
}
|
||||
Loading…
Reference in a new issue