blob: cac2b15bf3495d7ff3829c7144d2a512f2eb0abc [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/debug/zxdb/expr/resolve_base.h"
#include <gtest/gtest.h>
#include "llvm/BinaryFormat/Dwarf.h"
#include "src/developer/debug/zxdb/common/test_with_loop.h"
#include "src/developer/debug/zxdb/expr/abi_null.h"
#include "src/developer/debug/zxdb/expr/mock_eval_context.h"
#include "src/developer/debug/zxdb/expr/test_eval_context_impl.h"
#include "src/developer/debug/zxdb/expr/virtual_base_test_setup.h"
#include "src/developer/debug/zxdb/symbols/collection.h"
#include "src/developer/debug/zxdb/symbols/elf_symbol.h"
#include "src/developer/debug/zxdb/symbols/inherited_from.h"
#include "src/developer/debug/zxdb/symbols/location.h"
#include "src/developer/debug/zxdb/symbols/mock_module_symbols.h"
#include "src/developer/debug/zxdb/symbols/mock_symbol_data_provider.h"
#include "src/developer/debug/zxdb/symbols/modified_type.h"
#include "src/developer/debug/zxdb/symbols/process_symbols_test_setup.h"
#include "src/developer/debug/zxdb/symbols/type_test_support.h"
namespace zxdb {
namespace {
class ResolveBase : public TestWithLoop {};
} // namespace
// Given a class without a vtable, verifies that the DerivedTypeForVtable does a synchronous no-op.
TEST_F(ResolveBase, PromotePtrRefToDerived_NoVtable) {
auto eval_context = fxl::MakeRefCounted<MockEvalContext>();
auto not_virtual =
MakeCollectionType(DwarfTag::kStructureType, "MyStruct", {{"a", MakeInt32Type()}});
std::vector<uint8_t> data{42, 0, 0, 0}; // int32_t = 42.
ExprValue value(not_virtual, data);
bool called = false;
PromotePtrRefToDerived(eval_context, PromoteToDerived::kPtrOrRef, value,
[&called, original_value = value](ErrOrValue result) {
called = true;
EXPECT_TRUE(result.ok());
EXPECT_EQ(original_value, result.value());
// In this test the type object pointers should be the same (not normally
// tested in value equality) since the value should be the same one just
// forwarded.
EXPECT_EQ(original_value.type(), result.value().type());
});
EXPECT_TRUE(called);
}
TEST_F(ResolveBase, PromotePtrRefToDerived) {
ProcessSymbolsTestSetup symbol_setup;
MockModuleSymbols* mock_module_symbols = symbol_setup.InjectMockModule();
SymbolContext symbol_context(ProcessSymbolsTestSetup::kDefaultLoadAddress);
auto symbol_data_provider = fxl::MakeRefCounted<MockSymbolDataProvider>();
auto eval_context = fxl::MakeRefCounted<TestEvalContextImpl>(
std::make_shared<AbiNull>(), symbol_setup.process().GetWeakPtr(), symbol_data_provider,
ExprLanguage::kC);
VirtualBaseTestSetup setup(symbol_data_provider.get(), mock_module_symbols);
// Add a bunch of qualifiers to make sure they come out the other end.
auto const_base_class = fxl::MakeRefCounted<ModifiedType>(DwarfTag::kConstType, setup.base_class);
auto ptr_const_base_class =
fxl::MakeRefCounted<ModifiedType>(DwarfTag::kPointerType, const_base_class);
auto const_ptr_const_base_class =
fxl::MakeRefCounted<ModifiedType>(DwarfTag::kConstType, ptr_const_base_class);
// Input Base*.
ExprValue base_ptr(setup.kBaseAddress, const_ptr_const_base_class);
// -----------------------------------------------------------------------------------------------
// Part 1: vtable pointer points to "Derived".
// The default setup will have the vtable point to the derived class.
ErrOrValue result(Err("Not called"));
PromotePtrRefToDerived(eval_context, PromoteToDerived::kPtrOrRef, base_ptr,
[&result](ErrOrValue r) { result = r; });
loop().RunUntilNoTasks();
ASSERT_TRUE(result.ok());
// Now that the memory has been hooked up, the result should be a const*const (consts copied from
// the original base type) with the derived address.
uint64_t result64 = 0;
ASSERT_TRUE(result.value().PromoteTo64(&result64).ok());
EXPECT_EQ(setup.kDerivedAddress, result64);
EXPECT_EQ("const DerivedClass* const", result.value().type()->GetFullName());
// -----------------------------------------------------------------------------------------------
// Part 2: vtable pointer points to "Base".
// Fix up the vtable pointer to the base class.
Location vtable_location(setup.kVtableAbsoluteAddress, FileLine(), 0, symbol_context,
setup.base_vtable);
mock_module_symbols->AddSymbolLocations(setup.kVtableAbsoluteAddress, {vtable_location});
result = Err("Not called");
PromotePtrRefToDerived(eval_context, PromoteToDerived::kPtrOrRef, base_ptr,
[&result](ErrOrValue r) { result = r; });
loop().RunUntilNoTasks();
ASSERT_TRUE(result.ok()) << result.err().msg();
EXPECT_EQ(base_ptr, result.value()); // Should give same input as output.
// -----------------------------------------------------------------------------------------------
// Part 3: vtable pointer is invalid.
// Declare no symbol at this address.
mock_module_symbols->AddSymbolLocations(
setup.kVtableAbsoluteAddress,
{Location(Location::State::kSymbolized, setup.kVtableAbsoluteAddress)});
// Should run asynchronously and produce success.
result = Err("Not called");
PromotePtrRefToDerived(eval_context, PromoteToDerived::kPtrOrRef, base_ptr,
[&result](ErrOrValue r) { result = r; });
loop().RunUntilNoTasks();
ASSERT_TRUE(result.ok());
// We did not hook up the vtable memory above so the resolution will fail. It should fall back on
// returning the input rather than forwarding an error.
EXPECT_EQ(base_ptr, result.value());
EXPECT_EQ("const BaseClass* const", result.value().type()->GetFullName());
// -----------------------------------------------------------------------------------------------
// Part 4: virtual inheritance means we can't promote to derived.
// Put back the good derived vtable location cleared in the previous step so it will succeed.
mock_module_symbols->AddSymbolLocations(setup.kVtableAbsoluteAddress,
{Location(setup.kVtableAbsoluteAddress, FileLine(), 0,
symbol_context, setup.derived_vtable)});
// Replace the inheritance record with one indicating virtual inheritance. This placeholder
// expression won't work in practice (see VirtualInheritanceTestSetup for a real one) but the
// presence of some expression will trigger a casting failure.
auto virtual_inheritance =
fxl::MakeRefCounted<InheritedFrom>(setup.base_class, DwarfExpr({llvm::dwarf::DW_OP_dup}));
setup.derived_class->set_inherited_from({LazySymbol(virtual_inheritance)});
result = Err("Not called");
PromotePtrRefToDerived(eval_context, PromoteToDerived::kPtrOrRef, base_ptr,
[&result](ErrOrValue r) { result = r; });
loop().RunUntilNoTasks();
ASSERT_TRUE(result.ok()) << result.err().msg();
EXPECT_EQ(base_ptr, result.value()); // Should give same input as output.
}
} // namespace zxdb