245 lines
7.5 KiB
C++
245 lines
7.5 KiB
C++
//===-- UdtRecordCompleterTests.cpp ---------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "Plugins/SymbolFile/NativePDB/UdtRecordCompleter.h"
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace lldb_private::npdb;
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
using Member = UdtRecordCompleter::Member;
|
|
using MemberUP = std::unique_ptr<Member>;
|
|
using Record = UdtRecordCompleter::Record;
|
|
|
|
class WrappedMember {
|
|
public:
|
|
WrappedMember(const Member &obj) : m_obj(obj) {}
|
|
|
|
private:
|
|
const Member &m_obj;
|
|
|
|
friend bool operator==(const WrappedMember &lhs, const WrappedMember &rhs) {
|
|
return lhs.m_obj.kind == rhs.m_obj.kind &&
|
|
lhs.m_obj.name == rhs.m_obj.name &&
|
|
lhs.m_obj.bit_offset == rhs.m_obj.bit_offset &&
|
|
lhs.m_obj.bit_size == rhs.m_obj.bit_size &&
|
|
lhs.m_obj.base_offset == rhs.m_obj.base_offset &&
|
|
std::equal(lhs.m_obj.fields.begin(), lhs.m_obj.fields.end(),
|
|
rhs.m_obj.fields.begin(), rhs.m_obj.fields.end(),
|
|
[](const MemberUP &lhs, const MemberUP &rhs) {
|
|
return WrappedMember(*lhs) == WrappedMember(*rhs);
|
|
});
|
|
}
|
|
|
|
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
|
|
const WrappedMember &w) {
|
|
os << llvm::formatv("Member{.kind={0}, .name=\"{1}\", .bit_offset={2}, "
|
|
".bit_size={3}, .base_offset={4}, .fields=[",
|
|
w.m_obj.kind, w.m_obj.name, w.m_obj.bit_offset,
|
|
w.m_obj.bit_size, w.m_obj.base_offset);
|
|
llvm::ListSeparator sep;
|
|
for (auto &f : w.m_obj.fields)
|
|
os << sep << WrappedMember(*f);
|
|
return os << "]}";
|
|
}
|
|
};
|
|
|
|
class WrappedRecord {
|
|
public:
|
|
WrappedRecord(const Record &obj) : m_obj(obj) {}
|
|
|
|
private:
|
|
const Record &m_obj;
|
|
|
|
friend bool operator==(const WrappedRecord &lhs, const WrappedRecord &rhs) {
|
|
return lhs.m_obj.start_offset == rhs.m_obj.start_offset &&
|
|
std::equal(
|
|
lhs.m_obj.record.fields.begin(), lhs.m_obj.record.fields.end(),
|
|
rhs.m_obj.record.fields.begin(), rhs.m_obj.record.fields.end(),
|
|
[](const MemberUP &lhs, const MemberUP &rhs) {
|
|
return WrappedMember(*lhs) == WrappedMember(*rhs);
|
|
});
|
|
}
|
|
|
|
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
|
|
const WrappedRecord &w) {
|
|
os << llvm::formatv("Record{.start_offset={0}, .record.fields=[",
|
|
w.m_obj.start_offset);
|
|
llvm::ListSeparator sep;
|
|
for (const MemberUP &f : w.m_obj.record.fields)
|
|
os << sep << WrappedMember(*f);
|
|
return os << "]}";
|
|
}
|
|
};
|
|
|
|
class UdtRecordCompleterRecordTests : public testing::Test {
|
|
protected:
|
|
Record record;
|
|
|
|
public:
|
|
void SetKind(Member::Kind kind) { record.record.kind = kind; }
|
|
void CollectMember(StringRef name, uint64_t byte_offset, uint64_t byte_size) {
|
|
record.CollectMember(name, byte_offset * 8, byte_size * 8,
|
|
clang::QualType(), lldb::eAccessPublic, 0);
|
|
}
|
|
void ConstructRecord() { record.ConstructRecord(); }
|
|
};
|
|
Member *AddField(Member *member, StringRef name, uint64_t byte_offset,
|
|
uint64_t byte_size, Member::Kind kind,
|
|
uint64_t base_offset = 0) {
|
|
auto field =
|
|
std::make_unique<Member>(name, byte_offset * 8, byte_size * 8,
|
|
clang::QualType(), lldb::eAccessPublic, 0);
|
|
field->kind = kind;
|
|
field->base_offset = base_offset;
|
|
member->fields.push_back(std::move(field));
|
|
return member->fields.back().get();
|
|
}
|
|
} // namespace
|
|
|
|
TEST_F(UdtRecordCompleterRecordTests, TestAnonymousUnionInStruct) {
|
|
SetKind(Member::Kind::Struct);
|
|
CollectMember("m1", 0, 4);
|
|
CollectMember("m2", 0, 4);
|
|
CollectMember("m3", 0, 1);
|
|
CollectMember("m4", 0, 8);
|
|
ConstructRecord();
|
|
|
|
// struct {
|
|
// union {
|
|
// m1;
|
|
// m2;
|
|
// m3;
|
|
// m4;
|
|
// };
|
|
// };
|
|
Record record;
|
|
record.start_offset = 0;
|
|
Member *u = AddField(&record.record, "", 0, 0, Member::Union);
|
|
AddField(u, "m1", 0, 4, Member::Field);
|
|
AddField(u, "m2", 0, 4, Member::Field);
|
|
AddField(u, "m3", 0, 1, Member::Field);
|
|
AddField(u, "m4", 0, 8, Member::Field);
|
|
EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record));
|
|
}
|
|
|
|
TEST_F(UdtRecordCompleterRecordTests, TestAnonymousUnionInUnion) {
|
|
SetKind(Member::Kind::Union);
|
|
CollectMember("m1", 0, 4);
|
|
CollectMember("m2", 0, 4);
|
|
CollectMember("m3", 0, 1);
|
|
CollectMember("m4", 0, 8);
|
|
ConstructRecord();
|
|
|
|
// union {
|
|
// m1;
|
|
// m2;
|
|
// m3;
|
|
// m4;
|
|
// };
|
|
Record record;
|
|
record.start_offset = 0;
|
|
AddField(&record.record, "m1", 0, 4, Member::Field);
|
|
AddField(&record.record, "m2", 0, 4, Member::Field);
|
|
AddField(&record.record, "m3", 0, 1, Member::Field);
|
|
AddField(&record.record, "m4", 0, 8, Member::Field);
|
|
EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record));
|
|
}
|
|
|
|
TEST_F(UdtRecordCompleterRecordTests, TestAnonymousStructInUnion) {
|
|
SetKind(Member::Kind::Union);
|
|
CollectMember("m1", 0, 4);
|
|
CollectMember("m2", 4, 4);
|
|
CollectMember("m3", 8, 1);
|
|
ConstructRecord();
|
|
|
|
// union {
|
|
// struct {
|
|
// m1;
|
|
// m2;
|
|
// m3;
|
|
// };
|
|
// };
|
|
Record record;
|
|
record.start_offset = 0;
|
|
Member *s = AddField(&record.record, "", 0, 0, Member::Kind::Struct);
|
|
AddField(s, "m1", 0, 4, Member::Field);
|
|
AddField(s, "m2", 4, 4, Member::Field);
|
|
AddField(s, "m3", 8, 1, Member::Field);
|
|
EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record));
|
|
}
|
|
|
|
TEST_F(UdtRecordCompleterRecordTests, TestNestedUnionStructInStruct) {
|
|
SetKind(Member::Kind::Struct);
|
|
CollectMember("m1", 0, 4);
|
|
CollectMember("m2", 0, 2);
|
|
CollectMember("m3", 0, 2);
|
|
CollectMember("m4", 2, 4);
|
|
CollectMember("m5", 3, 2);
|
|
ConstructRecord();
|
|
|
|
// struct {
|
|
// union {
|
|
// m1;
|
|
// struct {
|
|
// m2;
|
|
// m5;
|
|
// };
|
|
// struct {
|
|
// m3;
|
|
// m4;
|
|
// };
|
|
// };
|
|
// };
|
|
Record record;
|
|
record.start_offset = 0;
|
|
Member *u = AddField(&record.record, "", 0, 0, Member::Union);
|
|
AddField(u, "m1", 0, 4, Member::Field);
|
|
Member *s1 = AddField(u, "", 0, 0, Member::Struct);
|
|
Member *s2 = AddField(u, "", 0, 0, Member::Struct);
|
|
AddField(s1, "m2", 0, 2, Member::Field);
|
|
AddField(s1, "m5", 3, 2, Member::Field);
|
|
AddField(s2, "m3", 0, 2, Member::Field);
|
|
AddField(s2, "m4", 2, 4, Member::Field);
|
|
EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record));
|
|
}
|
|
|
|
TEST_F(UdtRecordCompleterRecordTests, TestNestedUnionStructInUnion) {
|
|
SetKind(Member::Kind::Union);
|
|
CollectMember("m1", 0, 4);
|
|
CollectMember("m2", 0, 2);
|
|
CollectMember("m3", 0, 2);
|
|
CollectMember("m4", 2, 4);
|
|
CollectMember("m5", 3, 2);
|
|
ConstructRecord();
|
|
|
|
// union {
|
|
// m1;
|
|
// struct {
|
|
// m2;
|
|
// m5;
|
|
// };
|
|
// struct {
|
|
// m3;
|
|
// m4;
|
|
// };
|
|
// };
|
|
Record record;
|
|
record.start_offset = 0;
|
|
AddField(&record.record, "m1", 0, 4, Member::Field);
|
|
Member *s1 = AddField(&record.record, "", 0, 0, Member::Struct);
|
|
Member *s2 = AddField(&record.record, "", 0, 0, Member::Struct);
|
|
AddField(s1, "m2", 0, 2, Member::Field);
|
|
AddField(s1, "m5", 3, 2, Member::Field);
|
|
AddField(s2, "m3", 0, 2, Member::Field);
|
|
AddField(s2, "m4", 2, 4, Member::Field);
|
|
EXPECT_EQ(WrappedRecord(this->record), WrappedRecord(record));
|
|
}
|