1dd3a739dSZachary Turner //===- llvm/unittest/DebugInfo/CodeView/RandomAccessVisitorTest.cpp -------===//
2dd3a739dSZachary Turner //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6dd3a739dSZachary Turner //
7dd3a739dSZachary Turner //===----------------------------------------------------------------------===//
8dd3a739dSZachary Turner
9ca6dbf14SZachary Turner #include "llvm/DebugInfo/CodeView/AppendingTypeTableBuilder.h"
10dd3a739dSZachary Turner #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h"
11526f4f2aSZachary Turner #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
12dd3a739dSZachary Turner #include "llvm/DebugInfo/CodeView/TypeRecord.h"
13dd3a739dSZachary Turner #include "llvm/DebugInfo/CodeView/TypeRecordMapping.h"
14dd3a739dSZachary Turner #include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h"
15dd3a739dSZachary Turner #include "llvm/Support/Allocator.h"
16632ba9fcSReid Kleckner #include "llvm/Support/BinaryByteStream.h"
17dd3a739dSZachary Turner #include "llvm/Support/BinaryItemStream.h"
18dd3a739dSZachary Turner #include "llvm/Support/Error.h"
19cb30e705SZachary Turner #include "llvm/Testing/Support/Error.h"
20dd3a739dSZachary Turner
21dd3a739dSZachary Turner #include "gtest/gtest.h"
22dd3a739dSZachary Turner
23dd3a739dSZachary Turner using namespace llvm;
24dd3a739dSZachary Turner using namespace llvm::codeview;
25dd3a739dSZachary Turner
26dd3a739dSZachary Turner namespace llvm {
27dd3a739dSZachary Turner namespace codeview {
operator ==(const ArrayRecord & R1,const ArrayRecord & R2)28dd3a739dSZachary Turner inline bool operator==(const ArrayRecord &R1, const ArrayRecord &R2) {
29dd3a739dSZachary Turner if (R1.ElementType != R2.ElementType)
30dd3a739dSZachary Turner return false;
31dd3a739dSZachary Turner if (R1.IndexType != R2.IndexType)
32dd3a739dSZachary Turner return false;
33dd3a739dSZachary Turner if (R1.Name != R2.Name)
34dd3a739dSZachary Turner return false;
35dd3a739dSZachary Turner if (R1.Size != R2.Size)
36dd3a739dSZachary Turner return false;
37dd3a739dSZachary Turner return true;
38dd3a739dSZachary Turner }
operator !=(const ArrayRecord & R1,const ArrayRecord & R2)39dd3a739dSZachary Turner inline bool operator!=(const ArrayRecord &R1, const ArrayRecord &R2) {
40dd3a739dSZachary Turner return !(R1 == R2);
41dd3a739dSZachary Turner }
42dd3a739dSZachary Turner
operator ==(const CVType & R1,const CVType & R2)43dd3a739dSZachary Turner inline bool operator==(const CVType &R1, const CVType &R2) {
44dd3a739dSZachary Turner if (R1.RecordData != R2.RecordData)
45dd3a739dSZachary Turner return false;
46dd3a739dSZachary Turner return true;
47dd3a739dSZachary Turner }
operator !=(const CVType & R1,const CVType & R2)48dd3a739dSZachary Turner inline bool operator!=(const CVType &R1, const CVType &R2) {
49dd3a739dSZachary Turner return !(R1 == R2);
50dd3a739dSZachary Turner }
51dd3a739dSZachary Turner }
52dd3a739dSZachary Turner }
53dd3a739dSZachary Turner
54dd3a739dSZachary Turner namespace llvm {
55dd3a739dSZachary Turner template <> struct BinaryItemTraits<CVType> {
lengthllvm::BinaryItemTraits56dd3a739dSZachary Turner static size_t length(const CVType &Item) { return Item.length(); }
bytesllvm::BinaryItemTraits57dd3a739dSZachary Turner static ArrayRef<uint8_t> bytes(const CVType &Item) { return Item.data(); }
58dd3a739dSZachary Turner };
59dd3a739dSZachary Turner }
60dd3a739dSZachary Turner
61dd3a739dSZachary Turner namespace {
62dd3a739dSZachary Turner
63dd3a739dSZachary Turner class MockCallbacks : public TypeVisitorCallbacks {
64dd3a739dSZachary Turner public:
visitTypeBegin(CVType & CVR,TypeIndex Index)6531eb8349SLogan Smith Error visitTypeBegin(CVType &CVR, TypeIndex Index) override {
66dd3a739dSZachary Turner Indices.push_back(Index);
67dd3a739dSZachary Turner return Error::success();
68dd3a739dSZachary Turner }
visitKnownRecord(CVType & CVR,ArrayRecord & AR)6931eb8349SLogan Smith Error visitKnownRecord(CVType &CVR, ArrayRecord &AR) override {
70dd3a739dSZachary Turner VisitedRecords.push_back(AR);
71dd3a739dSZachary Turner RawRecords.push_back(CVR);
72dd3a739dSZachary Turner return Error::success();
73dd3a739dSZachary Turner }
74dd3a739dSZachary Turner
count() const75dd3a739dSZachary Turner uint32_t count() const {
76dd3a739dSZachary Turner assert(Indices.size() == RawRecords.size());
77dd3a739dSZachary Turner assert(Indices.size() == VisitedRecords.size());
78dd3a739dSZachary Turner return Indices.size();
79dd3a739dSZachary Turner }
80dd3a739dSZachary Turner std::vector<TypeIndex> Indices;
81dd3a739dSZachary Turner std::vector<CVType> RawRecords;
82dd3a739dSZachary Turner std::vector<ArrayRecord> VisitedRecords;
83dd3a739dSZachary Turner };
84dd3a739dSZachary Turner
85dd3a739dSZachary Turner class RandomAccessVisitorTest : public testing::Test {
86dd3a739dSZachary Turner public:
RandomAccessVisitorTest()87dd3a739dSZachary Turner RandomAccessVisitorTest() {}
88dd3a739dSZachary Turner
SetUpTestCase()89dd3a739dSZachary Turner static void SetUpTestCase() {
900eaee545SJonas Devlieghere GlobalState = std::make_unique<GlobalTestState>();
91dd3a739dSZachary Turner
92ca6dbf14SZachary Turner AppendingTypeTableBuilder Builder(GlobalState->Allocator);
93dd3a739dSZachary Turner
94dd3a739dSZachary Turner uint32_t Offset = 0;
95dd3a739dSZachary Turner for (int I = 0; I < 11; ++I) {
96dd3a739dSZachary Turner ArrayRecord AR(TypeRecordKind::Array);
97dd3a739dSZachary Turner AR.ElementType = TypeIndex::Int32();
98dd3a739dSZachary Turner AR.IndexType = TypeIndex::UInt32();
99dd3a739dSZachary Turner AR.Size = I;
100dd3a739dSZachary Turner std::string Name;
101dd3a739dSZachary Turner raw_string_ostream Stream(Name);
102dd3a739dSZachary Turner Stream << "Array [" << I << "]";
103dd3a739dSZachary Turner AR.Name = GlobalState->Strings.save(Stream.str());
104dd3a739dSZachary Turner GlobalState->Records.push_back(AR);
1056900de1dSZachary Turner GlobalState->Indices.push_back(Builder.writeLeafType(AR));
106dd3a739dSZachary Turner
107e10d0041SReid Kleckner CVType Type(Builder.records().back());
108dd3a739dSZachary Turner GlobalState->TypeVector.push_back(Type);
109dd3a739dSZachary Turner
110dd3a739dSZachary Turner GlobalState->AllOffsets.push_back(
111dd3a739dSZachary Turner {GlobalState->Indices.back(), ulittle32_t(Offset)});
112dd3a739dSZachary Turner Offset += Type.length();
113dd3a739dSZachary Turner }
114dd3a739dSZachary Turner
115dd3a739dSZachary Turner GlobalState->ItemStream.setItems(GlobalState->TypeVector);
116dd3a739dSZachary Turner GlobalState->TypeArray = VarStreamArray<CVType>(GlobalState->ItemStream);
117dd3a739dSZachary Turner }
118dd3a739dSZachary Turner
TearDownTestCase()119dd3a739dSZachary Turner static void TearDownTestCase() { GlobalState.reset(); }
120dd3a739dSZachary Turner
SetUp()121dd3a739dSZachary Turner void SetUp() override {
1220eaee545SJonas Devlieghere TestState = std::make_unique<PerTestState>();
123dd3a739dSZachary Turner }
124dd3a739dSZachary Turner
TearDown()125dd3a739dSZachary Turner void TearDown() override { TestState.reset(); }
126dd3a739dSZachary Turner
127dd3a739dSZachary Turner protected:
ValidateDatabaseRecord(LazyRandomTypeCollection & Types,uint32_t Index)128526f4f2aSZachary Turner bool ValidateDatabaseRecord(LazyRandomTypeCollection &Types, uint32_t Index) {
129dd3a739dSZachary Turner TypeIndex TI = TypeIndex::fromArrayIndex(Index);
130526f4f2aSZachary Turner if (!Types.contains(TI))
131dd3a739dSZachary Turner return false;
132526f4f2aSZachary Turner if (GlobalState->TypeVector[Index] != Types.getType(TI))
133dd3a739dSZachary Turner return false;
134dd3a739dSZachary Turner return true;
135dd3a739dSZachary Turner }
136dd3a739dSZachary Turner
ValidateVisitedRecord(uint32_t VisitationOrder,uint32_t GlobalArrayIndex)137dd3a739dSZachary Turner bool ValidateVisitedRecord(uint32_t VisitationOrder,
138dd3a739dSZachary Turner uint32_t GlobalArrayIndex) {
139dd3a739dSZachary Turner TypeIndex TI = TypeIndex::fromArrayIndex(GlobalArrayIndex);
140dd3a739dSZachary Turner if (TI != TestState->Callbacks.Indices[VisitationOrder])
141dd3a739dSZachary Turner return false;
142dd3a739dSZachary Turner
143dd3a739dSZachary Turner if (GlobalState->TypeVector[TI.toArrayIndex()] !=
144dd3a739dSZachary Turner TestState->Callbacks.RawRecords[VisitationOrder])
145dd3a739dSZachary Turner return false;
146dd3a739dSZachary Turner
147dd3a739dSZachary Turner if (GlobalState->Records[TI.toArrayIndex()] !=
148dd3a739dSZachary Turner TestState->Callbacks.VisitedRecords[VisitationOrder])
149dd3a739dSZachary Turner return false;
150dd3a739dSZachary Turner
151dd3a739dSZachary Turner return true;
152dd3a739dSZachary Turner }
153dd3a739dSZachary Turner
154dd3a739dSZachary Turner struct GlobalTestState {
GlobalTestState__anon7ae2edc90111::RandomAccessVisitorTest::GlobalTestState155b8885926SKazu Hirata GlobalTestState()
156b8885926SKazu Hirata : Strings(Allocator), ItemStream(llvm::endianness::little) {}
157dd3a739dSZachary Turner
158dd3a739dSZachary Turner BumpPtrAllocator Allocator;
159dd3a739dSZachary Turner StringSaver Strings;
160dd3a739dSZachary Turner
161dd3a739dSZachary Turner std::vector<ArrayRecord> Records;
162dd3a739dSZachary Turner std::vector<TypeIndex> Indices;
163dd3a739dSZachary Turner std::vector<TypeIndexOffset> AllOffsets;
164dd3a739dSZachary Turner std::vector<CVType> TypeVector;
165dd3a739dSZachary Turner BinaryItemStream<CVType> ItemStream;
166dd3a739dSZachary Turner VarStreamArray<CVType> TypeArray;
167dd3a739dSZachary Turner
168dd3a739dSZachary Turner MutableBinaryByteStream Stream;
169dd3a739dSZachary Turner };
170dd3a739dSZachary Turner
171dd3a739dSZachary Turner struct PerTestState {
172dd3a739dSZachary Turner FixedStreamArray<TypeIndexOffset> Offsets;
173dd3a739dSZachary Turner
174dd3a739dSZachary Turner MockCallbacks Callbacks;
175dd3a739dSZachary Turner };
176dd3a739dSZachary Turner
177dd3a739dSZachary Turner FixedStreamArray<TypeIndexOffset>
createPartialOffsets(MutableBinaryByteStream & Storage,std::initializer_list<uint32_t> Indices)178dd3a739dSZachary Turner createPartialOffsets(MutableBinaryByteStream &Storage,
179dd3a739dSZachary Turner std::initializer_list<uint32_t> Indices) {
180dd3a739dSZachary Turner
181dd3a739dSZachary Turner uint32_t Count = Indices.size();
182dd3a739dSZachary Turner uint32_t Size = Count * sizeof(TypeIndexOffset);
183dd3a739dSZachary Turner uint8_t *Buffer = GlobalState->Allocator.Allocate<uint8_t>(Size);
184dd3a739dSZachary Turner MutableArrayRef<uint8_t> Bytes(Buffer, Size);
185*4a0ccfa8SKazu Hirata Storage = MutableBinaryByteStream(Bytes, llvm::endianness::little);
186dd3a739dSZachary Turner BinaryStreamWriter Writer(Storage);
187dd3a739dSZachary Turner for (const auto I : Indices)
188dd3a739dSZachary Turner consumeError(Writer.writeObject(GlobalState->AllOffsets[I]));
189dd3a739dSZachary Turner
190dd3a739dSZachary Turner BinaryStreamReader Reader(Storage);
191dd3a739dSZachary Turner FixedStreamArray<TypeIndexOffset> Result;
192dd3a739dSZachary Turner consumeError(Reader.readArray(Result, Count));
193dd3a739dSZachary Turner return Result;
194dd3a739dSZachary Turner }
195dd3a739dSZachary Turner
196dd3a739dSZachary Turner static std::unique_ptr<GlobalTestState> GlobalState;
197dd3a739dSZachary Turner std::unique_ptr<PerTestState> TestState;
198dd3a739dSZachary Turner };
199dd3a739dSZachary Turner
200dd3a739dSZachary Turner std::unique_ptr<RandomAccessVisitorTest::GlobalTestState>
201dd3a739dSZachary Turner RandomAccessVisitorTest::GlobalState;
202dd3a739dSZachary Turner }
203dd3a739dSZachary Turner
TEST_F(RandomAccessVisitorTest,MultipleVisits)204dd3a739dSZachary Turner TEST_F(RandomAccessVisitorTest, MultipleVisits) {
205dd3a739dSZachary Turner TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
206526f4f2aSZachary Turner LazyRandomTypeCollection Types(GlobalState->TypeArray,
207dd3a739dSZachary Turner GlobalState->TypeVector.size(),
208dd3a739dSZachary Turner TestState->Offsets);
209dd3a739dSZachary Turner
210dd3a739dSZachary Turner std::vector<uint32_t> IndicesToVisit = {5, 5, 5};
211dd3a739dSZachary Turner
212dd3a739dSZachary Turner for (uint32_t I : IndicesToVisit) {
213dd3a739dSZachary Turner TypeIndex TI = TypeIndex::fromArrayIndex(I);
214526f4f2aSZachary Turner CVType T = Types.getType(TI);
215cb30e705SZachary Turner EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
216cb30e705SZachary Turner Succeeded());
217dd3a739dSZachary Turner }
218dd3a739dSZachary Turner
219dd3a739dSZachary Turner // [0,8) should be present
220526f4f2aSZachary Turner EXPECT_EQ(8u, Types.size());
221dd3a739dSZachary Turner for (uint32_t I = 0; I < 8; ++I)
222526f4f2aSZachary Turner EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
223dd3a739dSZachary Turner
224dd3a739dSZachary Turner // 5, 5, 5
225bce6d327SJustin Bogner EXPECT_EQ(3u, TestState->Callbacks.count());
226b9db89fbSJakub Kuderski for (const auto &I : enumerate(IndicesToVisit))
227dd3a739dSZachary Turner EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
228dd3a739dSZachary Turner }
229dd3a739dSZachary Turner
TEST_F(RandomAccessVisitorTest,DescendingWithinChunk)230dd3a739dSZachary Turner TEST_F(RandomAccessVisitorTest, DescendingWithinChunk) {
231dd3a739dSZachary Turner // Visit multiple items from the same "chunk" in reverse order. In this
232dd3a739dSZachary Turner // example, it's 7 then 4 then 2. At the end, all records from 0 to 7 should
233dd3a739dSZachary Turner // be known by the database, but only 2, 4, and 7 should have been visited.
234dd3a739dSZachary Turner TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
235dd3a739dSZachary Turner
236dd3a739dSZachary Turner std::vector<uint32_t> IndicesToVisit = {7, 4, 2};
237dd3a739dSZachary Turner
238526f4f2aSZachary Turner LazyRandomTypeCollection Types(GlobalState->TypeArray,
239dd3a739dSZachary Turner GlobalState->TypeVector.size(),
240dd3a739dSZachary Turner TestState->Offsets);
241dd3a739dSZachary Turner for (uint32_t I : IndicesToVisit) {
242dd3a739dSZachary Turner TypeIndex TI = TypeIndex::fromArrayIndex(I);
243526f4f2aSZachary Turner CVType T = Types.getType(TI);
244cb30e705SZachary Turner EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
245cb30e705SZachary Turner Succeeded());
246dd3a739dSZachary Turner }
247dd3a739dSZachary Turner
248dd3a739dSZachary Turner // [0, 7]
249526f4f2aSZachary Turner EXPECT_EQ(8u, Types.size());
250dd3a739dSZachary Turner for (uint32_t I = 0; I < 8; ++I)
251526f4f2aSZachary Turner EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
252dd3a739dSZachary Turner
253dd3a739dSZachary Turner // 2, 4, 7
254bce6d327SJustin Bogner EXPECT_EQ(3u, TestState->Callbacks.count());
255b9db89fbSJakub Kuderski for (const auto &I : enumerate(IndicesToVisit))
256dd3a739dSZachary Turner EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
257dd3a739dSZachary Turner }
258dd3a739dSZachary Turner
TEST_F(RandomAccessVisitorTest,AscendingWithinChunk)259dd3a739dSZachary Turner TEST_F(RandomAccessVisitorTest, AscendingWithinChunk) {
260dd3a739dSZachary Turner // * Visit multiple items from the same chunk in ascending order, ensuring
261dd3a739dSZachary Turner // that intermediate items are not visited. In the below example, it's
262dd3a739dSZachary Turner // 5 -> 6 -> 7 which come from the [4,8) chunk.
263dd3a739dSZachary Turner TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
264dd3a739dSZachary Turner
265dd3a739dSZachary Turner std::vector<uint32_t> IndicesToVisit = {2, 4, 7};
266dd3a739dSZachary Turner
267526f4f2aSZachary Turner LazyRandomTypeCollection Types(GlobalState->TypeArray,
268dd3a739dSZachary Turner GlobalState->TypeVector.size(),
269dd3a739dSZachary Turner TestState->Offsets);
270dd3a739dSZachary Turner for (uint32_t I : IndicesToVisit) {
271dd3a739dSZachary Turner TypeIndex TI = TypeIndex::fromArrayIndex(I);
272526f4f2aSZachary Turner CVType T = Types.getType(TI);
273cb30e705SZachary Turner EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
274cb30e705SZachary Turner Succeeded());
275dd3a739dSZachary Turner }
276dd3a739dSZachary Turner
277dd3a739dSZachary Turner // [0, 7]
278526f4f2aSZachary Turner EXPECT_EQ(8u, Types.size());
279dd3a739dSZachary Turner for (uint32_t I = 0; I < 8; ++I)
280526f4f2aSZachary Turner EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
281dd3a739dSZachary Turner
282dd3a739dSZachary Turner // 2, 4, 7
283bce6d327SJustin Bogner EXPECT_EQ(3u, TestState->Callbacks.count());
284b9db89fbSJakub Kuderski for (const auto &I : enumerate(IndicesToVisit))
285dd3a739dSZachary Turner EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
286dd3a739dSZachary Turner }
287dd3a739dSZachary Turner
TEST_F(RandomAccessVisitorTest,StopPrematurelyInChunk)288dd3a739dSZachary Turner TEST_F(RandomAccessVisitorTest, StopPrematurelyInChunk) {
289dd3a739dSZachary Turner // * Don't visit the last item in one chunk, ensuring that visitation stops
290dd3a739dSZachary Turner // at the record you specify, and the chunk is only partially visited.
291dd3a739dSZachary Turner // In the below example, this is tested by visiting 0 and 1 but not 2,
292dd3a739dSZachary Turner // all from the [0,3) chunk.
293dd3a739dSZachary Turner TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 8});
294dd3a739dSZachary Turner
295dd3a739dSZachary Turner std::vector<uint32_t> IndicesToVisit = {0, 1, 2};
296dd3a739dSZachary Turner
297526f4f2aSZachary Turner LazyRandomTypeCollection Types(GlobalState->TypeArray,
298dd3a739dSZachary Turner GlobalState->TypeVector.size(),
299dd3a739dSZachary Turner TestState->Offsets);
300dd3a739dSZachary Turner
301dd3a739dSZachary Turner for (uint32_t I : IndicesToVisit) {
302dd3a739dSZachary Turner TypeIndex TI = TypeIndex::fromArrayIndex(I);
303526f4f2aSZachary Turner CVType T = Types.getType(TI);
304cb30e705SZachary Turner EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
305cb30e705SZachary Turner Succeeded());
306dd3a739dSZachary Turner }
307dd3a739dSZachary Turner
308dd3a739dSZachary Turner // [0, 8) should be visited.
309526f4f2aSZachary Turner EXPECT_EQ(8u, Types.size());
310dd3a739dSZachary Turner for (uint32_t I = 0; I < 8; ++I)
311526f4f2aSZachary Turner EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
312dd3a739dSZachary Turner
313dd3a739dSZachary Turner // [0, 2]
314bce6d327SJustin Bogner EXPECT_EQ(3u, TestState->Callbacks.count());
315b9db89fbSJakub Kuderski for (const auto &I : enumerate(IndicesToVisit))
316dd3a739dSZachary Turner EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
317dd3a739dSZachary Turner }
318dd3a739dSZachary Turner
TEST_F(RandomAccessVisitorTest,InnerChunk)319dd3a739dSZachary Turner TEST_F(RandomAccessVisitorTest, InnerChunk) {
320dd3a739dSZachary Turner // Test that when a request comes from a chunk in the middle of the partial
321dd3a739dSZachary Turner // offsets array, that items from surrounding chunks are not visited or
322dd3a739dSZachary Turner // added to the database.
323dd3a739dSZachary Turner TestState->Offsets = createPartialOffsets(GlobalState->Stream, {0, 4, 9});
324dd3a739dSZachary Turner
325dd3a739dSZachary Turner std::vector<uint32_t> IndicesToVisit = {5, 7};
326dd3a739dSZachary Turner
327526f4f2aSZachary Turner LazyRandomTypeCollection Types(GlobalState->TypeArray,
328dd3a739dSZachary Turner GlobalState->TypeVector.size(),
329dd3a739dSZachary Turner TestState->Offsets);
330dd3a739dSZachary Turner
331dd3a739dSZachary Turner for (uint32_t I : IndicesToVisit) {
332dd3a739dSZachary Turner TypeIndex TI = TypeIndex::fromArrayIndex(I);
333526f4f2aSZachary Turner CVType T = Types.getType(TI);
334cb30e705SZachary Turner EXPECT_THAT_ERROR(codeview::visitTypeRecord(T, TI, TestState->Callbacks),
335cb30e705SZachary Turner Succeeded());
336dd3a739dSZachary Turner }
337dd3a739dSZachary Turner
338dd3a739dSZachary Turner // [4, 9)
339526f4f2aSZachary Turner EXPECT_EQ(5u, Types.size());
340dd3a739dSZachary Turner for (uint32_t I = 4; I < 9; ++I)
341526f4f2aSZachary Turner EXPECT_TRUE(ValidateDatabaseRecord(Types, I));
342dd3a739dSZachary Turner
343dd3a739dSZachary Turner // 5, 7
344bce6d327SJustin Bogner EXPECT_EQ(2u, TestState->Callbacks.count());
345b9db89fbSJakub Kuderski for (const auto &I : enumerate(IndicesToVisit))
346dd3a739dSZachary Turner EXPECT_TRUE(ValidateVisitedRecord(I.index(), I.value()));
347dd3a739dSZachary Turner }
348ad859bd4SZachary Turner
TEST_F(RandomAccessVisitorTest,CrossChunkName)349ad859bd4SZachary Turner TEST_F(RandomAccessVisitorTest, CrossChunkName) {
350ca6dbf14SZachary Turner AppendingTypeTableBuilder Builder(GlobalState->Allocator);
351ad859bd4SZachary Turner
352ad859bd4SZachary Turner // TypeIndex 0
353ad859bd4SZachary Turner ClassRecord Class(TypeRecordKind::Class);
354ad859bd4SZachary Turner Class.Name = "FooClass";
355ad859bd4SZachary Turner Class.Options = ClassOptions::None;
356ad859bd4SZachary Turner Class.MemberCount = 0;
357a56e4ee3SZachary Turner Class.Size = 4U;
358ad859bd4SZachary Turner Class.DerivationList = TypeIndex::fromArrayIndex(0);
359ad859bd4SZachary Turner Class.FieldList = TypeIndex::fromArrayIndex(0);
360ad859bd4SZachary Turner Class.VTableShape = TypeIndex::fromArrayIndex(0);
3616900de1dSZachary Turner TypeIndex IndexZero = Builder.writeLeafType(Class);
362ad859bd4SZachary Turner
363ad859bd4SZachary Turner // TypeIndex 1 refers to type index 0.
364ad859bd4SZachary Turner ModifierRecord Modifier(TypeRecordKind::Modifier);
365ad859bd4SZachary Turner Modifier.ModifiedType = TypeIndex::fromArrayIndex(0);
366ad859bd4SZachary Turner Modifier.Modifiers = ModifierOptions::Const;
3676900de1dSZachary Turner TypeIndex IndexOne = Builder.writeLeafType(Modifier);
368ad859bd4SZachary Turner
369ad859bd4SZachary Turner // set up a type stream that refers to the above two serialized records.
370e10d0041SReid Kleckner std::vector<CVType> TypeArray = {
371e10d0041SReid Kleckner {Builder.records()[0]},
372e10d0041SReid Kleckner {Builder.records()[1]},
373e10d0041SReid Kleckner };
374b8885926SKazu Hirata BinaryItemStream<CVType> ItemStream(llvm::endianness::little);
375ad859bd4SZachary Turner ItemStream.setItems(TypeArray);
376ad859bd4SZachary Turner VarStreamArray<CVType> TypeStream(ItemStream);
377ad859bd4SZachary Turner
378ad859bd4SZachary Turner // Figure out the byte offset of the second item.
379ad859bd4SZachary Turner auto ItemOneIter = TypeStream.begin();
380ad859bd4SZachary Turner ++ItemOneIter;
381ad859bd4SZachary Turner
382ad859bd4SZachary Turner // Set up a partial offsets buffer that contains the first and second items
383ad859bd4SZachary Turner // in separate chunks.
384ad859bd4SZachary Turner std::vector<TypeIndexOffset> TIO;
385ad859bd4SZachary Turner TIO.push_back({IndexZero, ulittle32_t(0u)});
386ad859bd4SZachary Turner TIO.push_back({IndexOne, ulittle32_t(ItemOneIter.offset())});
387ad859bd4SZachary Turner ArrayRef<uint8_t> Buffer(reinterpret_cast<const uint8_t *>(TIO.data()),
388ad859bd4SZachary Turner TIO.size() * sizeof(TypeIndexOffset));
389ad859bd4SZachary Turner
390b8885926SKazu Hirata BinaryStreamReader Reader(Buffer, llvm::endianness::little);
391ad859bd4SZachary Turner FixedStreamArray<TypeIndexOffset> PartialOffsets;
392ad859bd4SZachary Turner ASSERT_THAT_ERROR(Reader.readArray(PartialOffsets, 2), Succeeded());
393ad859bd4SZachary Turner
394ad859bd4SZachary Turner LazyRandomTypeCollection Types(TypeStream, 2, PartialOffsets);
395ad859bd4SZachary Turner
396ad859bd4SZachary Turner StringRef Name = Types.getTypeName(IndexOne);
397ad859bd4SZachary Turner EXPECT_EQ("const FooClass", Name);
398ad859bd4SZachary Turner }
399