• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "annotation_data_accessor.h"
17 #include "class_data_accessor-inl.h"
18 #include "code_data_accessor-inl.h"
19 #include "debug_data_accessor-inl.h"
20 #include "field_data_accessor-inl.h"
21 #include "file.h"
22 #include "file_item_container.h"
23 #include "file_reader.h"
24 #include "file_writer.h"
25 #include "helpers.h"
26 #include "method_data_accessor-inl.h"
27 #include "method_handle_data_accessor.h"
28 #include "modifiers.h"
29 #include "os/file.h"
30 #include "proto_data_accessor-inl.h"
31 #include "pgo.h"
32 #include "value.h"
33 
34 #include "zlib.h"
35 
36 #include <cstddef>
37 
38 #include <memory>
39 #include <vector>
40 
41 #include <gtest/gtest.h>
42 #include <gmock/gmock.h>
43 
44 namespace ark::panda_file::test {
45 
TEST(ItemContainer,DeduplicationTest)46 TEST(ItemContainer, DeduplicationTest)
47 {
48     ItemContainer container;
49 
50     StringItem *stringItem = container.GetOrCreateStringItem("1");
51     EXPECT_EQ(stringItem, container.GetOrCreateStringItem("1"));
52 
53     ClassItem *classItem = container.GetOrCreateClassItem("1");
54     EXPECT_EQ(classItem, container.GetOrCreateClassItem("1"));
55 
56     ValueItem *intItem = container.GetOrCreateIntegerValueItem(1);
57     EXPECT_EQ(intItem, container.GetOrCreateIntegerValueItem(1));
58 
59     ValueItem *longItem = container.GetOrCreateLongValueItem(1);
60     EXPECT_EQ(longItem, container.GetOrCreateLongValueItem(1));
61     EXPECT_NE(longItem, intItem);
62 
63     ValueItem *floatItem = container.GetOrCreateFloatValueItem(1.0);
64     EXPECT_EQ(floatItem, container.GetOrCreateFloatValueItem(1.0));
65     EXPECT_NE(floatItem, intItem);
66     EXPECT_NE(floatItem, longItem);
67 
68     ValueItem *doubleItem = container.GetOrCreateDoubleValueItem(1.0);
69     EXPECT_EQ(doubleItem, container.GetOrCreateDoubleValueItem(1.0));
70     EXPECT_NE(doubleItem, intItem);
71     EXPECT_NE(doubleItem, longItem);
72     EXPECT_NE(doubleItem, floatItem);
73 }
74 
TEST(ItemContainer,TestFileOpen)75 TEST(ItemContainer, TestFileOpen)
76 {
77     using ark::os::file::Mode;
78     using ark::os::file::Open;
79 
80     // Write panda file to disk
81     ItemContainer container;
82 
83     const std::string fileName = "test_file_open.panda";
84     auto writer = FileWriter(fileName);
85 
86     ASSERT_TRUE(container.Write(&writer));
87 
88     // Read panda file from disk
89     EXPECT_NE(File::Open(fileName), nullptr);
90 }
91 
TEST(ItemContainer,TestFileFormatVersionTooOld)92 TEST(ItemContainer, TestFileFormatVersionTooOld)
93 {
94     const std::string fileName = "test_file_format_version_too_old.abc";
95     {
96         ItemContainer container;
97         auto writer = FileWriter(fileName);
98 
99         File::Header header {};
100         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
101         std::fill(reinterpret_cast<uint8_t *>(&header), reinterpret_cast<uint8_t *>(&header) + sizeof(header), 0);
102         header.magic = File::MAGIC;
103 
104         auto old = std::array<uint8_t, File::VERSION_SIZE>(MIN_VERSION);
105         --old[3];
106 
107         header.version = old;
108         header.fileSize = sizeof(File::Header);
109 
110         for (uint8_t b : Span<uint8_t>(reinterpret_cast<uint8_t *>(&header), sizeof(header))) {
111             writer.WriteByte(b);
112         }
113     }
114 
115     EXPECT_EQ(File::Open(fileName), nullptr);
116 }
117 
TEST(ItemContainer,TestFileFormatVersionTooNew)118 TEST(ItemContainer, TestFileFormatVersionTooNew)
119 {
120     const std::string fileName = "test_file_format_version_too_new.abc";
121     {
122         ItemContainer container;
123         auto writer = FileWriter(fileName);
124 
125         File::Header header {};
126         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
127         std::fill(reinterpret_cast<uint8_t *>(&header), reinterpret_cast<uint8_t *>(&header) + sizeof(header), 0);
128         header.magic = File::MAGIC;
129 
130         auto newArr = std::array<uint8_t, File::VERSION_SIZE>(VERSION);
131         ++newArr[3];
132 
133         header.version = newArr;
134         header.fileSize = sizeof(File::Header);
135 
136         for (uint8_t b : Span<uint8_t>(reinterpret_cast<uint8_t *>(&header), sizeof(header))) {
137             writer.WriteByte(b);
138         }
139     }
140 
141     EXPECT_EQ(File::Open(fileName), nullptr);
142 }
143 
TEST(ItemContainer,TestFileFormatVersionValid)144 TEST(ItemContainer, TestFileFormatVersionValid)
145 {
146     const std::string fileName = "test_file_format_version_valid.abc";
147     {
148         ItemContainer container;
149         auto writer = FileWriter(fileName);
150         File::Header header {};
151         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
152         std::fill(reinterpret_cast<uint8_t *>(&header), reinterpret_cast<uint8_t *>(&header) + sizeof(header), 0);
153         header.magic = File::MAGIC;
154         header.version = {0, 0, 0, 5};
155         header.fileSize = sizeof(File::Header);
156         auto pChecksumData = reinterpret_cast<uint8_t *>(&header.version);
157         auto checksum = adler32(1, pChecksumData, sizeof(header) - offsetof(File::Header, version));
158         header.checksum = checksum;
159         for (uint8_t b : Span<uint8_t>(reinterpret_cast<uint8_t *>(&header), sizeof(header))) {
160             writer.WriteByte(b);
161         }
162     }
163 
164     EXPECT_NE(File::Open(fileName), nullptr);
165 }
166 
GetPandaFile(std::vector<uint8_t> & data)167 static std::unique_ptr<const File> GetPandaFile(std::vector<uint8_t> &data)
168 {
169     os::mem::ConstBytePtr ptr(reinterpret_cast<std::byte *>(data.data()), data.size(),
170                               [](std::byte *, size_t) noexcept {});
171     return File::OpenFromMemory(std::move(ptr));
172 }
173 
174 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
TEST(ItemContainer,TestClasses)175 TEST(ItemContainer, TestClasses)
176 {
177     // Write panda file to memory
178 
179     ItemContainer container;
180 
181     ClassItem *emptyClassItem = container.GetOrCreateClassItem("Foo");
182 
183     ClassItem *classItem = container.GetOrCreateClassItem("Bar");
184     classItem->SetAccessFlags(ACC_PUBLIC);
185     classItem->SetSuperClass(emptyClassItem);
186 
187     // Add interface
188 
189     ClassItem *ifaceItem = container.GetOrCreateClassItem("Iface");
190     ifaceItem->SetAccessFlags(ACC_PUBLIC);
191 
192     classItem->AddInterface(ifaceItem);
193 
194     // Add method
195 
196     StringItem *methodName = container.GetOrCreateStringItem("foo");
197 
198     PrimitiveTypeItem *retType = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
199     std::vector<MethodParamItem> params;
200     ProtoItem *protoItem = container.GetOrCreateProtoItem(retType, params);
201 
202     MethodItem *methodItem = classItem->AddMethod(methodName, protoItem, ACC_PUBLIC | ACC_STATIC, params);
203 
204     // Add field
205 
206     StringItem *fieldName = container.GetOrCreateStringItem("field");
207     PrimitiveTypeItem *fieldType = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32);
208 
209     FieldItem *fieldItem = classItem->AddField(fieldName, fieldType, ACC_PUBLIC);
210 
211     // Add runtime annotation
212 
213     std::vector<AnnotationItem::Elem> runtimeElems;
214     std::vector<AnnotationItem::Tag> runtimeTags;
215     auto *runtimeAnnotationItem = container.CreateItem<AnnotationItem>(classItem, runtimeElems, runtimeTags);
216 
217     classItem->AddRuntimeAnnotation(runtimeAnnotationItem);
218 
219     // Add annotation
220 
221     std::vector<AnnotationItem::Elem> elems;
222     std::vector<AnnotationItem::Tag> tags;
223     auto *annotationItem = container.CreateItem<AnnotationItem>(classItem, elems, tags);
224 
225     classItem->AddAnnotation(annotationItem);
226 
227     // Add source file
228 
229     StringItem *sourceFile = container.GetOrCreateStringItem("source_file");
230 
231     classItem->SetSourceFile(sourceFile);
232 
233     MemoryWriter memWriter;
234 
235     ASSERT_TRUE(container.Write(&memWriter));
236 
237     // Read panda file from memory
238 
239     auto data = memWriter.GetData();
240     auto pandaFile = GetPandaFile(data);
241 
242     ASSERT_NE(pandaFile, nullptr);
243 
244     EXPECT_THAT(pandaFile->GetHeader()->version, ::testing::ElementsAre(0, 0, 0, 5));
245     EXPECT_EQ(pandaFile->GetHeader()->fileSize, memWriter.GetData().size());
246     EXPECT_EQ(pandaFile->GetHeader()->foreignOff, 0U);
247     EXPECT_EQ(pandaFile->GetHeader()->foreignSize, 0U);
248     EXPECT_EQ(pandaFile->GetHeader()->numClasses, 3U);
249     EXPECT_EQ(pandaFile->GetHeader()->classIdxOff, sizeof(File::Header));
250 
251     // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
252     const auto *classIndex =
253         reinterpret_cast<const uint32_t *>(pandaFile->GetBase() + pandaFile->GetHeader()->classIdxOff);
254     EXPECT_EQ(classIndex[0], classItem->GetOffset());
255     EXPECT_EQ(classIndex[1], emptyClassItem->GetOffset());
256     // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
257 
258     std::vector<uint8_t> className {'B', 'a', 'r', '\0'};
259     auto classId = pandaFile->GetClassId(className.data());
260     EXPECT_EQ(classId.GetOffset(), classItem->GetOffset());
261 
262     ClassDataAccessor classDataAccessor(*pandaFile, classId);
263     EXPECT_EQ(classDataAccessor.GetSuperClassId().GetOffset(), emptyClassItem->GetOffset());
264     EXPECT_EQ(classDataAccessor.GetAccessFlags(), ACC_PUBLIC);
265     EXPECT_EQ(classDataAccessor.GetFieldsNumber(), 1U);
266     EXPECT_EQ(classDataAccessor.GetMethodsNumber(), 1U);
267     EXPECT_EQ(classDataAccessor.GetIfacesNumber(), 1U);
268     EXPECT_TRUE(classDataAccessor.GetSourceFileId().has_value());
269     EXPECT_EQ(classDataAccessor.GetSourceFileId().value().GetOffset(), sourceFile->GetOffset());
270     EXPECT_EQ(classDataAccessor.GetSize(), classItem->GetSize());
271 
272     classDataAccessor.EnumerateInterfaces([&](File::EntityId id) {
273         EXPECT_EQ(id.GetOffset(), ifaceItem->GetOffset());
274 
275         ClassDataAccessor ifaceClassDataAccessor(*pandaFile, id);
276         EXPECT_EQ(ifaceClassDataAccessor.GetSuperClassId().GetOffset(), 0U);
277         EXPECT_EQ(ifaceClassDataAccessor.GetAccessFlags(), ACC_PUBLIC);
278         EXPECT_EQ(ifaceClassDataAccessor.GetFieldsNumber(), 0U);
279         EXPECT_EQ(ifaceClassDataAccessor.GetMethodsNumber(), 0U);
280         EXPECT_EQ(ifaceClassDataAccessor.GetIfacesNumber(), 0U);
281         EXPECT_FALSE(ifaceClassDataAccessor.GetSourceFileId().has_value());
282         EXPECT_EQ(ifaceClassDataAccessor.GetSize(), ifaceItem->GetSize());
283     });
284 
285     classDataAccessor.EnumerateRuntimeAnnotations([&](File::EntityId id) {
286         EXPECT_EQ(id.GetOffset(), runtimeAnnotationItem->GetOffset());
287 
288         AnnotationDataAccessor dataAccessor(*pandaFile, id);
289         EXPECT_EQ(dataAccessor.GetAnnotationId().GetOffset(), runtimeAnnotationItem->GetOffset());
290         EXPECT_EQ(dataAccessor.GetClassId().GetOffset(), classItem->GetOffset());
291         EXPECT_EQ(dataAccessor.GetCount(), 0U);
292     });
293 
294     // Annotation is the same as the runtime one, so we deduplicate it
295     EXPECT_FALSE(annotationItem->NeedsEmit());
296     annotationItem = runtimeAnnotationItem;
297 
298     classDataAccessor.EnumerateAnnotations([&](File::EntityId id) {
299         EXPECT_EQ(id.GetOffset(), annotationItem->GetOffset());
300 
301         AnnotationDataAccessor dataAccessor(*pandaFile, id);
302         EXPECT_EQ(dataAccessor.GetAnnotationId().GetOffset(), annotationItem->GetOffset());
303         EXPECT_EQ(dataAccessor.GetClassId().GetOffset(), classItem->GetOffset());
304         EXPECT_EQ(dataAccessor.GetCount(), 0U);
305     });
306 
307     classDataAccessor.EnumerateFields([&](FieldDataAccessor &dataAccessor) {
308         EXPECT_EQ(dataAccessor.GetFieldId().GetOffset(), fieldItem->GetOffset());
309         EXPECT_EQ(dataAccessor.GetClassId().GetOffset(), classItem->GetOffset());
310         EXPECT_EQ(dataAccessor.GetNameId().GetOffset(), fieldName->GetOffset());
311         EXPECT_EQ(dataAccessor.GetType(), Type(Type::TypeId::I32).GetFieldEncoding());
312         EXPECT_EQ(dataAccessor.GetAccessFlags(), ACC_PUBLIC);
313         EXPECT_FALSE(dataAccessor.GetValue<int32_t>().has_value());
314         EXPECT_EQ(dataAccessor.GetSize(), fieldItem->GetSize());
315 
316         dataAccessor.EnumerateRuntimeAnnotations([](File::EntityId) { EXPECT_TRUE(false); });
317         dataAccessor.EnumerateAnnotations([](File::EntityId) { EXPECT_TRUE(false); });
318     });
319 
320     classDataAccessor.EnumerateMethods([&](MethodDataAccessor &dataAccessor) {
321         EXPECT_FALSE(dataAccessor.IsExternal());
322         EXPECT_EQ(dataAccessor.GetMethodId().GetOffset(), methodItem->GetOffset());
323         EXPECT_EQ(dataAccessor.GetClassId().GetOffset(), classItem->GetOffset());
324         EXPECT_EQ(dataAccessor.GetNameId().GetOffset(), methodName->GetOffset());
325         EXPECT_EQ(dataAccessor.GetProtoId().GetOffset(), protoItem->GetOffset());
326         EXPECT_EQ(dataAccessor.GetAccessFlags(), ACC_PUBLIC | ACC_STATIC);
327         EXPECT_FALSE(dataAccessor.GetCodeId().has_value());
328         EXPECT_EQ(dataAccessor.GetSize(), methodItem->GetSize());
329         EXPECT_FALSE(dataAccessor.GetRuntimeParamAnnotationId().has_value());
330         EXPECT_FALSE(dataAccessor.GetParamAnnotationId().has_value());
331         EXPECT_FALSE(dataAccessor.GetDebugInfoId().has_value());
332 
333         dataAccessor.EnumerateRuntimeAnnotations([](File::EntityId) { EXPECT_TRUE(false); });
334         dataAccessor.EnumerateAnnotations([](File::EntityId) { EXPECT_TRUE(false); });
335     });
336 
337     ClassDataAccessor emptyClassDataAccessor(*pandaFile, File::EntityId(emptyClassItem->GetOffset()));
338     EXPECT_EQ(emptyClassDataAccessor.GetSuperClassId().GetOffset(), 0U);
339     EXPECT_EQ(emptyClassDataAccessor.GetAccessFlags(), 0U);
340     EXPECT_EQ(emptyClassDataAccessor.GetFieldsNumber(), 0U);
341     EXPECT_EQ(emptyClassDataAccessor.GetMethodsNumber(), 0U);
342     EXPECT_EQ(emptyClassDataAccessor.GetIfacesNumber(), 0U);
343     EXPECT_FALSE(emptyClassDataAccessor.GetSourceFileId().has_value());
344     EXPECT_EQ(emptyClassDataAccessor.GetSize(), emptyClassItem->GetSize());
345 }
346 
TEST(ItemContainer,TestMethods)347 TEST(ItemContainer, TestMethods)
348 {
349     // Write panda file to memory
350 
351     ItemContainer container;
352 
353     ClassItem *classItem = container.GetOrCreateClassItem("A");
354     classItem->SetAccessFlags(ACC_PUBLIC);
355 
356     StringItem *methodName = container.GetOrCreateStringItem("foo");
357 
358     PrimitiveTypeItem *retType = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
359     std::vector<MethodParamItem> params;
360     ProtoItem *protoItem = container.GetOrCreateProtoItem(retType, params);
361 
362     MethodItem *methodItem = classItem->AddMethod(methodName, protoItem, ACC_PUBLIC | ACC_STATIC, params);
363 
364     std::vector<uint8_t> instructions {1, 2, 3, 4};
365     auto *codeItem = container.CreateItem<CodeItem>(0, 2, instructions);
366 
367     methodItem->SetCode(codeItem);
368 
369     MemoryWriter memWriter;
370 
371     ASSERT_TRUE(container.Write(&memWriter));
372 
373     // Read panda file from memory
374 
375     auto data = memWriter.GetData();
376     auto pandaFile = GetPandaFile(data);
377 
378     ASSERT_NE(pandaFile, nullptr);
379 
380     ClassDataAccessor classDataAccessor(*pandaFile, File::EntityId(classItem->GetOffset()));
381 
382     classDataAccessor.EnumerateMethods([&](MethodDataAccessor &dataAccessor) {
383         EXPECT_FALSE(dataAccessor.IsExternal());
384         EXPECT_EQ(dataAccessor.GetMethodId().GetOffset(), methodItem->GetOffset());
385         EXPECT_EQ(dataAccessor.GetClassId().GetOffset(), classItem->GetOffset());
386         EXPECT_EQ(dataAccessor.GetNameId().GetOffset(), methodName->GetOffset());
387         EXPECT_EQ(dataAccessor.GetProtoId().GetOffset(), protoItem->GetOffset());
388         EXPECT_EQ(dataAccessor.GetAccessFlags(), ACC_PUBLIC | ACC_STATIC);
389         EXPECT_EQ(dataAccessor.GetSize(), methodItem->GetSize());
390 
391         auto codeId = dataAccessor.GetCodeId();
392         EXPECT_TRUE(codeId.has_value());
393         EXPECT_EQ(codeId.value().GetOffset(), codeItem->GetOffset());
394 
395         CodeDataAccessor codeDataAccessor(*pandaFile, codeId.value());
396         EXPECT_EQ(codeDataAccessor.GetNumVregs(), 0U);
397         EXPECT_EQ(codeDataAccessor.GetNumArgs(), 2U);
398         EXPECT_EQ(codeDataAccessor.GetCodeSize(), instructions.size());
399         EXPECT_THAT(instructions,
400                     ::testing::ElementsAreArray(codeDataAccessor.GetInstructions(), codeDataAccessor.GetCodeSize()));
401 
402         EXPECT_EQ(codeDataAccessor.GetTriesSize(), 0U);
403         EXPECT_EQ(codeDataAccessor.GetSize(), codeItem->GetSize());
404 
405         codeDataAccessor.EnumerateTryBlocks([](const CodeDataAccessor::TryBlock &) {
406             EXPECT_TRUE(false);
407             return false;
408         });
409 
410         EXPECT_FALSE(dataAccessor.GetDebugInfoId().has_value());
411 
412         EXPECT_FALSE(dataAccessor.GetRuntimeParamAnnotationId().has_value());
413 
414         EXPECT_FALSE(dataAccessor.GetParamAnnotationId().has_value());
415 
416         dataAccessor.EnumerateRuntimeAnnotations([](File::EntityId) { EXPECT_TRUE(false); });
417 
418         dataAccessor.EnumerateAnnotations([](File::EntityId) { EXPECT_TRUE(false); });
419     });
420 }
421 
422 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
TestProtos(size_t n)423 void TestProtos(size_t n)
424 {
425     constexpr size_t ELEM_WIDTH = 4;
426     constexpr size_t ELEM_PER16 = 16 / ELEM_WIDTH;
427 
428     // Write panda file to memory
429 
430     ItemContainer container;
431 
432     ClassItem *classItem = container.GetOrCreateClassItem("A");
433     classItem->SetAccessFlags(ACC_PUBLIC);
434 
435     StringItem *methodName = container.GetOrCreateStringItem("foo");
436 
437     std::vector<Type::TypeId> types {Type::TypeId::VOID, Type::TypeId::I32};
438     std::vector<ClassItem *> refTypes;
439 
440     PrimitiveTypeItem *retType = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
441     std::vector<MethodParamItem> params;
442 
443     params.emplace_back(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32));
444 
445     for (size_t i = 0; i < ELEM_PER16 * 2U - 2U; i++) {
446         params.emplace_back(container.GetOrCreateClassItem("B"));
447         types.push_back(Type::TypeId::REFERENCE);
448         refTypes.push_back(container.GetOrCreateClassItem("B"));
449         params.emplace_back(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::F64));
450         types.push_back(Type::TypeId::F64);
451     }
452 
453     for (size_t i = 0; i < n; i++) {
454         params.emplace_back(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::F32));
455         types.push_back(Type::TypeId::F32);
456     }
457 
458     ProtoItem *protoItem = container.GetOrCreateProtoItem(retType, params);
459 
460     MethodItem *methodItem = classItem->AddMethod(methodName, protoItem, ACC_PUBLIC | ACC_STATIC, params);
461 
462     MemoryWriter memWriter;
463 
464     ASSERT_TRUE(container.Write(&memWriter));
465 
466     // Read panda file from memory
467 
468     auto data = memWriter.GetData();
469     auto pandaFile = GetPandaFile(data);
470 
471     ASSERT_NE(pandaFile, nullptr);
472 
473     ClassDataAccessor classDataAccessor(*pandaFile, File::EntityId(classItem->GetOffset()));
474 
475     classDataAccessor.EnumerateMethods([&](MethodDataAccessor &dataAccessor) {
476         EXPECT_EQ(dataAccessor.GetMethodId().GetOffset(), methodItem->GetOffset());
477         EXPECT_EQ(dataAccessor.GetProtoId().GetOffset(), protoItem->GetOffset());
478 
479         ProtoDataAccessor protoDataAccessor(*pandaFile, dataAccessor.GetProtoId());
480         EXPECT_EQ(protoDataAccessor.GetProtoId().GetOffset(), protoItem->GetOffset());
481 
482         size_t num = 0;
483         size_t nref = 0;
484         protoDataAccessor.EnumerateTypes([&](Type t) {
485             EXPECT_EQ(t.GetEncoding(), Type(types[num]).GetEncoding());
486             ++num;
487 
488             if (!t.IsPrimitive()) {
489                 ++nref;
490             }
491         });
492 
493         EXPECT_EQ(num, types.size());
494 
495         for (size_t i = 0; i < num - 1; i++) {
496             EXPECT_EQ(protoDataAccessor.GetArgType(i).GetEncoding(), Type(types[i + 1]).GetEncoding());
497         }
498 
499         EXPECT_EQ(protoDataAccessor.GetReturnType().GetEncoding(), Type(types[0]).GetEncoding());
500 
501         EXPECT_EQ(nref, refTypes.size());
502 
503         for (size_t i = 0; i < nref; i++) {
504             EXPECT_EQ(protoDataAccessor.GetReferenceType(0).GetOffset(), refTypes[i]->GetOffset());
505         }
506 
507         size_t size = ((num + ELEM_PER16) / ELEM_PER16 + nref) * sizeof(uint16_t);
508 
509         EXPECT_EQ(protoDataAccessor.GetSize(), size);
510         EXPECT_EQ(protoDataAccessor.GetSize(), protoItem->GetSize());
511     });
512 }
513 
TEST(ItemContainer,TestProtos)514 TEST(ItemContainer, TestProtos)
515 {
516     TestProtos(0);
517     TestProtos(1);
518     TestProtos(2);
519     TestProtos(7);
520 }
521 
522 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
TEST(ItemContainer,TestDebugInfo)523 TEST(ItemContainer, TestDebugInfo)
524 {
525     // Write panda file to memory
526 
527     ItemContainer container;
528 
529     ClassItem *classItem = container.GetOrCreateClassItem("A");
530     classItem->SetAccessFlags(ACC_PUBLIC);
531 
532     StringItem *methodName = container.GetOrCreateStringItem("foo");
533 
534     PrimitiveTypeItem *retType = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
535     std::vector<MethodParamItem> params;
536     params.emplace_back(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32));
537     ProtoItem *protoItem = container.GetOrCreateProtoItem(retType, params);
538     MethodItem *methodItem = classItem->AddMethod(methodName, protoItem, ACC_PUBLIC | ACC_STATIC, params);
539 
540     StringItem *sourceFileItem = container.GetOrCreateStringItem("<source>");
541     StringItem *sourceCodeItem = container.GetOrCreateStringItem("let a = 1;");
542     StringItem *paramStringItem = container.GetOrCreateStringItem("a0");
543 
544     LineNumberProgramItem *lineNumberProgramItem = container.CreateLineNumberProgramItem();
545     auto *debugInfoItem = container.CreateItem<DebugInfoItem>(lineNumberProgramItem);
546     methodItem->SetDebugInfo(debugInfoItem);
547 
548     // Add debug info
549 
550     container.ComputeLayout();
551 
552     std::vector<uint8_t> opcodes {
553         static_cast<uint8_t>(LineNumberProgramItem::Opcode::SET_SOURCE_CODE),
554         static_cast<uint8_t>(LineNumberProgramItem::Opcode::SET_FILE),
555         static_cast<uint8_t>(LineNumberProgramItem::Opcode::SET_PROLOGUE_END),
556         static_cast<uint8_t>(LineNumberProgramItem::Opcode::ADVANCE_PC),
557         static_cast<uint8_t>(LineNumberProgramItem::Opcode::ADVANCE_LINE),
558         static_cast<uint8_t>(LineNumberProgramItem::Opcode::SET_EPILOGUE_BEGIN),
559         static_cast<uint8_t>(LineNumberProgramItem::Opcode::END_SEQUENCE),
560     };
561 
562     auto *constantPool = debugInfoItem->GetConstantPool();
563     debugInfoItem->SetLineNumber(5);
564     lineNumberProgramItem->EmitSetSourceCode(constantPool, sourceCodeItem);
565     lineNumberProgramItem->EmitSetFile(constantPool, sourceFileItem);
566     lineNumberProgramItem->EmitPrologueEnd();
567     // NOLINTNEXTLINE(readability-magic-numbers)
568     lineNumberProgramItem->EmitAdvancePc(constantPool, 10);
569     lineNumberProgramItem->EmitAdvanceLine(constantPool, -5);
570     lineNumberProgramItem->EmitEpilogueBegin();
571     lineNumberProgramItem->EmitEnd();
572 
573     debugInfoItem->AddParameter(paramStringItem);
574 
575     methodItem->SetDebugInfo(debugInfoItem);
576 
577     MemoryWriter memWriter;
578 
579     ASSERT_TRUE(container.Write(&memWriter));
580 
581     // Read panda file from memory
582 
583     auto data = memWriter.GetData();
584     auto pandaFile = GetPandaFile(data);
585 
586     ASSERT_NE(pandaFile, nullptr);
587 
588     ClassDataAccessor classDataAccessor(*pandaFile, File::EntityId(classItem->GetOffset()));
589 
590     classDataAccessor.EnumerateMethods([&](MethodDataAccessor &dataAccessor) {
591         EXPECT_EQ(dataAccessor.GetMethodId().GetOffset(), methodItem->GetOffset());
592         EXPECT_EQ(dataAccessor.GetSize(), methodItem->GetSize());
593 
594         auto debugInfoId = dataAccessor.GetDebugInfoId();
595         EXPECT_TRUE(debugInfoId.has_value());
596 
597         EXPECT_EQ(debugInfoId.value().GetOffset(), debugInfoItem->GetOffset());
598 
599         DebugInfoDataAccessor dda(*pandaFile, debugInfoId.value());
600         EXPECT_EQ(dda.GetDebugInfoId().GetOffset(), debugInfoItem->GetOffset());
601         EXPECT_EQ(dda.GetLineStart(), 5U);
602         EXPECT_EQ(dda.GetNumParams(), params.size());
603 
604         dda.EnumerateParameters([&](File::EntityId id) { EXPECT_EQ(id.GetOffset(), paramStringItem->GetOffset()); });
605 
606         auto cp = dda.GetConstantPool();
607         EXPECT_EQ(cp.size(), constantPool->size());
608         EXPECT_THAT(*constantPool, ::testing::ElementsAreArray(cp.data(), cp.Size()));
609 
610         EXPECT_EQ(helpers::ReadULeb128(&cp), sourceCodeItem->GetOffset());
611         EXPECT_EQ(helpers::ReadULeb128(&cp), sourceFileItem->GetOffset());
612         EXPECT_EQ(helpers::ReadULeb128(&cp), 10U);
613         EXPECT_EQ(helpers::ReadLeb128(&cp), -5);
614 
615         const uint8_t *lineNumberProgram = dda.GetLineNumberProgram();
616         EXPECT_EQ(pandaFile->GetIdFromPointer(lineNumberProgram).GetOffset(), lineNumberProgramItem->GetOffset());
617         EXPECT_EQ(lineNumberProgramItem->GetSize(), opcodes.size());
618 
619         EXPECT_THAT(opcodes, ::testing::ElementsAreArray(lineNumberProgram, opcodes.size()));
620 
621         EXPECT_EQ(dda.GetSize(), debugInfoItem->GetSize());
622     });
623 }
624 
TEST(ItemContainer,ForeignItems)625 TEST(ItemContainer, ForeignItems)
626 {
627     ItemContainer container;
628 
629     // Create foreign class
630     ForeignClassItem *classItem = container.GetOrCreateForeignClassItem("ForeignClass");
631 
632     // Create foreign field
633     StringItem *fieldName = container.GetOrCreateStringItem("foreign_field");
634     PrimitiveTypeItem *fieldType = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32);
635     auto *fieldItem = container.CreateItem<ForeignFieldItem>(classItem, fieldName, fieldType, 0);
636 
637     // Create foreign method
638     StringItem *methodName = container.GetOrCreateStringItem("ForeignMethod");
639     PrimitiveTypeItem *retType = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
640     std::vector<MethodParamItem> params;
641     params.emplace_back(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32));
642     ProtoItem *protoItem = container.GetOrCreateProtoItem(retType, params);
643     auto *methodItem = container.CreateItem<ForeignMethodItem>(classItem, methodName, protoItem, 0);
644 
645     MemoryWriter memWriter;
646 
647     ASSERT_TRUE(container.Write(&memWriter));
648 
649     // Read panda file from memory
650 
651     auto data = memWriter.GetData();
652     auto pandaFile = GetPandaFile(data);
653 
654     ASSERT_NE(pandaFile, nullptr);
655 
656     EXPECT_EQ(pandaFile->GetHeader()->foreignOff, classItem->GetOffset());
657 
658     size_t foreignSize = classItem->GetSize() + fieldItem->GetSize() + methodItem->GetSize();
659     EXPECT_EQ(pandaFile->GetHeader()->foreignSize, foreignSize);
660 
661     ASSERT_TRUE(pandaFile->IsExternal(classItem->GetFileId()));
662 
663     MethodDataAccessor methodDataAccessor(*pandaFile, methodItem->GetFileId());
664     EXPECT_EQ(methodDataAccessor.GetMethodId().GetOffset(), methodItem->GetOffset());
665     EXPECT_EQ(methodDataAccessor.GetSize(), methodItem->GetSize());
666     EXPECT_EQ(methodDataAccessor.GetClassId().GetOffset(), classItem->GetOffset());
667     EXPECT_EQ(methodDataAccessor.GetNameId().GetOffset(), methodName->GetOffset());
668     EXPECT_EQ(methodDataAccessor.GetProtoId().GetOffset(), protoItem->GetOffset());
669     EXPECT_TRUE(methodDataAccessor.IsExternal());
670 
671     FieldDataAccessor fieldDataAccessor(*pandaFile, fieldItem->GetFileId());
672     EXPECT_EQ(fieldDataAccessor.GetFieldId().GetOffset(), fieldItem->GetOffset());
673     EXPECT_EQ(fieldDataAccessor.GetSize(), fieldItem->GetSize());
674     EXPECT_EQ(fieldDataAccessor.GetClassId().GetOffset(), classItem->GetOffset());
675     EXPECT_EQ(fieldDataAccessor.GetNameId().GetOffset(), fieldName->GetOffset());
676     EXPECT_EQ(fieldDataAccessor.GetType(), fieldType->GetType().GetFieldEncoding());
677     EXPECT_TRUE(fieldDataAccessor.IsExternal());
678 }
679 
TEST(ItemContainer,EmptyContainerChecksum)680 TEST(ItemContainer, EmptyContainerChecksum)
681 {
682     using ark::os::file::Mode;
683     using ark::os::file::Open;
684 
685     // Write panda file to disk
686     ItemContainer container;
687 
688     const std::string fileName = "test_empty_checksum.ark";
689     auto writer = FileWriter(fileName);
690 
691     // Initial value of adler32
692     EXPECT_EQ(writer.GetChecksum(), 1);
693     ASSERT_TRUE(container.Write(&writer));
694 
695     // At least header was written so the checksum should be changed
696     auto containerChecksum = writer.GetChecksum();
697     EXPECT_NE(containerChecksum, 1);
698 
699     // Read panda file from disk
700     auto file = File::Open(fileName);
701     EXPECT_NE(file, nullptr);
702     EXPECT_EQ(file->GetHeader()->checksum, containerChecksum);
703 
704     constexpr size_t DATA_OFFSET = 12U;
705     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
706     auto checksum = adler32(1, file->GetBase() + DATA_OFFSET, file->GetHeader()->fileSize - DATA_OFFSET);
707     EXPECT_EQ(file->GetHeader()->checksum, checksum);
708 }
709 
TEST(ItemContainer,ContainerChecksum)710 TEST(ItemContainer, ContainerChecksum)
711 {
712     using ark::os::file::Mode;
713     using ark::os::file::Open;
714 
715     uint32_t emptyChecksum = 0;
716     {
717         ItemContainer container;
718         const std::string fileName = "test_checksum_empty.ark";
719         auto writer = FileWriter(fileName);
720         ASSERT_TRUE(container.Write(&writer));
721         emptyChecksum = writer.GetChecksum();
722     }
723     ASSERT(emptyChecksum != 0);
724 
725     // Create not empty container
726     ItemContainer container;
727     container.GetOrCreateClassItem("C");
728 
729     const std::string fileName = "test_checksum.ark";
730     auto writer = FileWriter(fileName);
731 
732     ASSERT_TRUE(container.Write(&writer));
733 
734     // This checksum must be different from the empty one (collision may happen though)
735     auto containerChecksum = writer.GetChecksum();
736     EXPECT_NE(emptyChecksum, containerChecksum);
737 
738     // Read panda file from disk
739     auto file = File::Open(fileName);
740     EXPECT_NE(file, nullptr);
741     EXPECT_EQ(file->GetHeader()->checksum, containerChecksum);
742 
743     constexpr size_t DATA_OFFSET = 12U;
744     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
745     auto checksum = adler32(1, file->GetBase() + DATA_OFFSET, file->GetHeader()->fileSize - DATA_OFFSET);
746     EXPECT_EQ(file->GetHeader()->checksum, checksum);
747 }
748 
749 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
TEST(ItemContainer,TestProfileGuidedRelayout)750 TEST(ItemContainer, TestProfileGuidedRelayout)
751 {
752     ItemContainer container;
753 
754     // Add classes
755     ClassItem *emptyClassItem = container.GetOrCreateClassItem("LTest;");
756     ClassItem *classItemA = container.GetOrCreateClassItem("LAA;");
757     classItemA->SetSuperClass(emptyClassItem);
758     ClassItem *classItemB = container.GetOrCreateClassItem("LBB;");
759 
760     // Add method1
761     StringItem *methodName1 = container.GetOrCreateStringItem("foo1");
762     PrimitiveTypeItem *retType1 = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
763     std::vector<MethodParamItem> params1;
764     ProtoItem *protoItem1 = container.GetOrCreateProtoItem(retType1, params1);
765     MethodItem *methodItem1 = classItemA->AddMethod(methodName1, protoItem1, ACC_PUBLIC | ACC_STATIC, params1);
766     // Set code_1
767     std::vector<uint8_t> instructions1 {1, 2, 3, 4};
768     auto *codeItem1 = container.CreateItem<CodeItem>(0, 2, instructions1);
769     methodItem1->SetCode(codeItem1);
770     codeItem1->AddMethod(methodItem1);
771 
772     // Add method2
773     StringItem *methodName2 = container.GetOrCreateStringItem("foo2");
774     PrimitiveTypeItem *retType2 = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32);
775     std::vector<MethodParamItem> params2;
776     ProtoItem *protoItem2 = container.GetOrCreateProtoItem(retType2, params2);
777     MethodItem *methodItem2 = classItemB->AddMethod(methodName2, protoItem2, ACC_PUBLIC | ACC_STATIC, params2);
778     // Set code_2
779     std::vector<uint8_t> instructions2 {5, 6, 7, 8};
780     auto *codeItem2 = container.CreateItem<CodeItem>(0, 2, instructions2);
781     methodItem2->SetCode(codeItem2);
782     codeItem2->AddMethod(methodItem2);
783 
784     // Add method_3
785     StringItem *methodName3 = container.GetOrCreateStringItem("foo3");
786     auto *methodItem3 = emptyClassItem->AddMethod(methodName3, protoItem1, ACC_PUBLIC | ACC_STATIC, params1);
787     // Set code_3
788     std::vector<uint8_t> instructions3 {3, 4, 5, 6};
789     auto *codeItem3 = container.CreateItem<CodeItem>(0, 2, instructions3);
790     methodItem3->SetCode(codeItem3);
791     codeItem3->AddMethod(methodItem3);
792 
793     // Add method_4
794     StringItem *methodName4 = container.GetOrCreateStringItem("foo4");
795     auto *methodItem4 = emptyClassItem->AddMethod(methodName4, protoItem1, ACC_PUBLIC | ACC_STATIC, params1);
796     // Set code. method_4 and method_3 share code_item_3
797     methodItem4->SetCode(codeItem3);
798     codeItem3->AddMethod(methodItem4);
799 
800     // Add field
801     StringItem *fieldName = container.GetOrCreateStringItem("test_field");
802     PrimitiveTypeItem *fieldType = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32);
803     classItemA->AddField(fieldName, fieldType, ACC_PUBLIC);
804 
805     // Add source file
806     StringItem *sourceFile = container.GetOrCreateStringItem("source_file");
807     classItemA->SetSourceFile(sourceFile);
808 
809     constexpr std::string_view PRIMITIVE_TYPE_ITEM = "primitive_type_item";
810     constexpr std::string_view PROTO_ITEM = "proto_item";
811     constexpr std::string_view END_ITEM = "end_item";
812 
813     // Items before PGO
814     const auto &items = container.GetItems();
815     auto item = items.begin();
816     EXPECT_EQ((*item)->GetName(), CLASS_ITEM);
817     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "Test");
818     item++;
819     EXPECT_EQ((*item)->GetName(), CLASS_ITEM);
820     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "AA");
821     item++;
822     EXPECT_EQ((*item)->GetName(), CLASS_ITEM);
823     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "BB");
824     item++;
825     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
826     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo1");
827     item++;
828     EXPECT_EQ((*item)->GetName(), PRIMITIVE_TYPE_ITEM);
829     item++;
830     EXPECT_EQ((*item)->GetName(), PROTO_ITEM);
831     item++;
832     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
833     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo2");
834     item++;
835     EXPECT_EQ((*item)->GetName(), PRIMITIVE_TYPE_ITEM);
836     item++;
837     EXPECT_EQ((*item)->GetName(), PROTO_ITEM);
838     item++;
839     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
840     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo3");
841     item++;
842     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
843     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo4");
844     item++;
845     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
846     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "test_field");
847     item++;
848     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
849     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "source_file");
850     item++;
851     EXPECT_EQ((*item)->GetName(), END_ITEM);
852     item++;
853     EXPECT_EQ((*item)->GetName(), END_ITEM);
854     item++;
855     EXPECT_EQ((*item)->GetName(), CODE_ITEM);
856     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[0], "AA::foo1");
857     item++;
858     EXPECT_EQ((*item)->GetName(), CODE_ITEM);
859     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[0], "BB::foo2");
860     item++;
861     EXPECT_EQ((*item)->GetName(), CODE_ITEM);
862     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[0], "Test::foo3");
863     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[1], "Test::foo4");
864     item++;
865     EXPECT_EQ((*item)->GetName(), END_ITEM);
866     item++;
867     EXPECT_EQ((*item)->GetName(), END_ITEM);
868     item++;
869     EXPECT_EQ(item, items.end());
870 
871     // Prepare profile data
872     std::string profilePath = "TestProfileGuidedRelayout_profile_test_data.txt";
873     std::ofstream testFile;
874     testFile.open(profilePath);
875     testFile << "string_item:test_field" << std::endl;
876     testFile << "class_item:BB" << std::endl;
877     testFile << "code_item:BB::foo2" << std::endl;
878     testFile << "code_item:Test::foo4" << std::endl;
879     testFile.close();
880 
881     // Run PGO
882     ark::panda_file::pgo::ProfileOptimizer profileOpt;
883     profileOpt.SetProfilePath(profilePath);
884     container.ReorderItems(&profileOpt);
885 
886     // Items after PGO
887     item = items.begin();
888     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
889     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "test_field");
890     item++;
891     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
892     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo1");
893     item++;
894     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
895     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo2");
896     item++;
897     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
898     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo3");
899     item++;
900     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
901     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo4");
902     item++;
903     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
904     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "source_file");
905     item++;
906     EXPECT_EQ((*item)->GetName(), CLASS_ITEM);
907     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "BB");
908     item++;
909     EXPECT_EQ((*item)->GetName(), CLASS_ITEM);
910     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "Test");
911     item++;
912     EXPECT_EQ((*item)->GetName(), CLASS_ITEM);
913     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "AA");
914     item++;
915     EXPECT_EQ((*item)->GetName(), PRIMITIVE_TYPE_ITEM);
916     item++;
917     EXPECT_EQ((*item)->GetName(), PROTO_ITEM);
918     item++;
919     EXPECT_EQ((*item)->GetName(), PRIMITIVE_TYPE_ITEM);
920     item++;
921     EXPECT_EQ((*item)->GetName(), PROTO_ITEM);
922     item++;
923     EXPECT_EQ((*item)->GetName(), END_ITEM);
924     item++;
925     EXPECT_EQ((*item)->GetName(), END_ITEM);
926     item++;
927     EXPECT_EQ((*item)->GetName(), END_ITEM);
928     item++;
929     EXPECT_EQ((*item)->GetName(), END_ITEM);
930     item++;
931     EXPECT_EQ((*item)->GetName(), CODE_ITEM);
932     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[0], "BB::foo2");
933     item++;
934     EXPECT_EQ((*item)->GetName(), CODE_ITEM);
935     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[0], "Test::foo3");
936     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[1], "Test::foo4");
937     item++;
938     EXPECT_EQ((*item)->GetName(), CODE_ITEM);
939     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[0], "AA::foo1");
940     item++;
941     EXPECT_EQ(item, items.end());
942 }
943 
944 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
TEST(ItemContainer,GettersTest)945 TEST(ItemContainer, GettersTest)
946 {
947     ItemContainer container;
948 
949     ClassItem *emptyClassItem = container.GetOrCreateClassItem("Foo");
950 
951     ClassItem *classItem = container.GetOrCreateClassItem("Bar");
952     classItem->SetAccessFlags(ACC_PUBLIC);
953     classItem->SetSuperClass(emptyClassItem);
954 
955     // Add methods
956 
957     StringItem *methodName1 = container.GetOrCreateStringItem("foo1");
958 
959     PrimitiveTypeItem *retType1 = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
960     std::vector<MethodParamItem> params1;
961     params1.emplace_back(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32));
962     ProtoItem *protoItem1 = container.GetOrCreateProtoItem(retType1, params1);
963 
964     classItem->AddMethod(methodName1, protoItem1, ACC_PUBLIC | ACC_STATIC, params1);
965 
966     StringItem *methodName2 = container.GetOrCreateStringItem("foo2");
967 
968     PrimitiveTypeItem *retType2 = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32);
969     std::vector<MethodParamItem> params2;
970     params2.emplace_back(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::F32));
971     ProtoItem *protoItem2 = container.GetOrCreateProtoItem(retType2, params2);
972 
973     classItem->AddMethod(methodName2, protoItem2, ACC_PUBLIC | ACC_STATIC, params2);
974 
975     // Add field
976 
977     StringItem *fieldName = container.GetOrCreateStringItem("field");
978     PrimitiveTypeItem *fieldType = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32);
979 
980     classItem->AddField(fieldName, fieldType, ACC_PUBLIC);
981 
982     // Add source file
983 
984     StringItem *sourceFile = container.GetOrCreateStringItem("source_file");
985 
986     classItem->SetSourceFile(sourceFile);
987 
988     // Read items from container
989 
990     ASSERT_TRUE(container.GetItems().size() == 15);
991 
992     std::map<std::string, panda_file::BaseClassItem *> *classMap = container.GetClassMap();
993     ASSERT_TRUE(classMap != nullptr && classMap->size() == 2);
994     auto it = classMap->find("Bar");
995     ASSERT_TRUE(it != classMap->end());
996 
997     std::unordered_map<std::string, StringItem *> *stringMap = container.GetStringMap();
998     ASSERT_TRUE(stringMap != nullptr && stringMap->size() == 4);
999     auto sit0 = stringMap->find("field");
1000     auto sit1 = stringMap->find("source_file");
1001     auto sit2 = stringMap->find("foo1");
1002     auto sit3 = stringMap->find("foo2");
1003     ASSERT_TRUE(sit0 != stringMap->end() && sit1 != stringMap->end() && sit2 != stringMap->end() &&
1004                 sit3 != stringMap->end());
1005 
1006     std::unordered_map<Type::TypeId, PrimitiveTypeItem *> *primitiveTypeMap = container.GetPrimitiveTypeMap();
1007     ASSERT_TRUE(primitiveTypeMap != nullptr && primitiveTypeMap->size() == 3);
1008     auto pit0 = primitiveTypeMap->find(Type::TypeId::F32);
1009     auto pit1 = primitiveTypeMap->find(Type::TypeId::I32);
1010     auto pit2 = primitiveTypeMap->find(Type::TypeId::VOID);
1011     ASSERT_TRUE(pit0 != primitiveTypeMap->end() && pit1 != primitiveTypeMap->end() && pit2 != primitiveTypeMap->end());
1012 
1013     auto *rclassItem = static_cast<panda_file::ClassItem *>(it->second);
1014     std::string methodName;
1015     std::function<bool(BaseItem *)> testMethod = [&](BaseItem *method) {
1016         auto *methodItem = static_cast<panda_file::MethodItem *>(method);
1017         ASSERT(methodItem != nullptr && methodItem->GetItemType() == ItemTypes::METHOD_ITEM);
1018         methodName = methodItem->GetNameItem()->GetData();
1019         methodName.pop_back();  // remove '\0'
1020         ASSERT(methodName == "foo1" || methodName == "foo2");
1021         return true;
1022     };
1023 
1024     using std::placeholders::_1;
1025     panda_file::BaseItem::VisitorCallBack cbMethod = testMethod;
1026     rclassItem->VisitMethods(cbMethod);
1027 
1028     std::string fName;
1029     std::function<bool(BaseItem *)> testField = [&](BaseItem *field) {
1030         auto *fieldItem = static_cast<panda_file::FieldItem *>(field);
1031         ASSERT(fieldItem != nullptr && fieldItem->GetItemType() == ItemTypes::FIELD_ITEM);
1032         ASSERT(fieldItem->GetBaseItemType() == ItemTypes::ANNOTATION_ITEM);
1033         fieldItem->SetBaseItemType(ItemTypes::FIELD_ITEM);
1034         ASSERT(fieldItem->GetBaseItemType() == ItemTypes::FIELD_ITEM);
1035         fName = fieldItem->GetNameItem()->GetData();
1036         fName.pop_back();  // remove '\0'
1037         ASSERT(fName == "field");
1038         return true;
1039     };
1040 
1041     panda_file::BaseItem::VisitorCallBack cbField = testField;
1042     rclassItem->VisitFields(cbField);
1043 }
1044 
TEST(ItemContainer,AnnotationDeduplication)1045 TEST(ItemContainer, AnnotationDeduplication)
1046 {
1047     ItemContainer container;
1048 
1049     auto annot = container.GetOrCreateClassItem("Annot");
1050     annot->SetAccessFlags(ACC_PUBLIC | ACC_ANNOTATION);
1051     auto annot1 = container.CreateItem<AnnotationItem>(annot, std::vector<AnnotationItem::Elem> {},
1052                                                        std::vector<AnnotationItem::Tag> {});
1053     auto annot2 = container.CreateItem<AnnotationItem>(annot, std::vector<AnnotationItem::Elem> {},
1054                                                        std::vector<AnnotationItem::Tag> {});
1055 
1056     auto str = container.GetOrCreateStringItem("Abc");
1057 
1058     auto glb = container.GetOrCreateGlobalClassItem();
1059     auto proto = container.GetOrCreateProtoItem(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID), {});
1060     auto meth = glb->AddMethod(container.GetOrCreateStringItem("foo"), proto, ACC_STATIC | ACC_PUBLIC,
1061                                std::vector<MethodParamItem> {});
1062     meth->AddAnnotation(annot1);
1063     meth->AddAnnotation(annot2);
1064 
1065     std::vector<uint8_t> ins = {(uint8_t)BytecodeInstruction::Opcode::LDA_STR_ID32, 0, 0, 0, 0,
1066                                 (uint8_t)BytecodeInstruction::Opcode::RETURN_VOID};
1067     meth->SetCode(container.CreateItem<CodeItem>(0, 0, ins));
1068 
1069     container.ComputeLayout();
1070     for (size_t i = 0; i < 4; i++) {
1071         // NOLINTBEGIN(readability-magic-numbers)
1072         meth->GetCode()->GetInstructions()->at(1 + i) =
1073             static_cast<uint8_t>((str->GetFileId().GetOffset() >> (i * 8)) & 0xffU);
1074         // NOLINTEND(readability-magic-numbers)
1075     }
1076     container.DeduplicateItems(false);
1077 
1078     ASSERT_TRUE(!annot1->NeedsEmit() || !annot2->NeedsEmit());
1079 
1080     container.ComputeLayout();
1081     for (size_t i = 0; i < 4; i++) {
1082         // NOLINTBEGIN(readability-magic-numbers)
1083         ASSERT_EQ(meth->GetCode()->GetInstructions()->at(1 + i),
1084                   static_cast<uint8_t>((str->GetFileId().GetOffset() >> (i * 8)) & 0xffU));
1085         // NOLINTEND(readability-magic-numbers)
1086     }
1087 }
1088 
TEST(ItemContainer,AnnotationDeduplicationReading)1089 TEST(ItemContainer, AnnotationDeduplicationReading)
1090 {
1091     ItemContainer container;
1092 
1093     auto annot = container.GetOrCreateClassItem("Annot");
1094     annot->SetAccessFlags(ACC_PUBLIC | ACC_ANNOTATION);
1095     auto annot1 = container.CreateItem<AnnotationItem>(annot, std::vector<AnnotationItem::Elem> {},
1096                                                        std::vector<AnnotationItem::Tag> {});
1097     auto annot2 = container.CreateItem<AnnotationItem>(annot, std::vector<AnnotationItem::Elem> {},
1098                                                        std::vector<AnnotationItem::Tag> {});
1099 
1100     auto glb = container.GetOrCreateGlobalClassItem();
1101 
1102     glb->AddAnnotation(annot1);
1103     glb->AddAnnotation(annot2);
1104 
1105     size_t size = container.ComputeLayout();
1106 
1107     container.DeduplicateAnnotations();
1108     ASSERT_FALSE(annot1->NeedsEmit() && annot2->NeedsEmit());
1109     ASSERT_EQ(glb->GetAnnotations()->at(0), glb->GetAnnotations()->at(1));
1110     container.DeduplicateAnnotations();
1111 
1112     std::vector<uint8_t> memBuf(size);
1113     MemoryBufferWriter writer {memBuf.data(), memBuf.size()};
1114     ASSERT_EQ(glb->GetAnnotations()->at(0), glb->GetAnnotations()->at(1));
1115     container.Write(&writer);
1116 
1117     auto emptyDeleter = +[](std::byte *, size_t) noexcept {};
1118     auto reader = FileReader(File::OpenFromMemory(
1119         os::mem::ConstBytePtr(reinterpret_cast<std::byte *>(memBuf.data()), memBuf.size(), emptyDeleter)));
1120     ASSERT_TRUE(reader.ReadContainer());
1121 
1122     auto annots = reader.GetContainerPtr()->GetOrCreateGlobalClassItem()->GetAnnotations();
1123     ASSERT_EQ(annots->size(), 2);
1124     ASSERT_EQ(annots->at(0), annots->at(1));
1125     auto annot3 = reader.GetContainerPtr()->CreateItem<AnnotationItem>(
1126         reader.GetContainerPtr()->GetOrCreateClassItem("Annot"), std::vector<AnnotationItem::Elem> {},
1127         std::vector<AnnotationItem::Tag> {});
1128     annots->insert(annots->begin(), annot3);
1129     reader.GetContainerPtr()->DeduplicateItems(true);
1130     reader.GetContainerPtr()->DeduplicateItems(true);
1131 }
1132 
TEST(ItemContainer,CodeSizeZeroShouldFatal)1133 TEST(ItemContainer, CodeSizeZeroShouldFatal)
1134 {
1135     EXPECT_DEATH(
1136         {
1137             ItemContainer container;
1138             auto *klass = container.GetOrCreateClassItem("DeathTest_ZeroCodeSize");
1139             auto *methodName = container.GetOrCreateStringItem("test_method_zero");
1140             auto *proto =
1141                 container.GetOrCreateProtoItem(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID), {});
1142             auto *method =
1143                 klass->AddMethod(methodName, proto, ACC_PUBLIC | ACC_STATIC, std::vector<MethodParamItem> {});
1144 
1145             std::vector<uint8_t> code {};
1146             auto *codeItem = container.CreateItem<CodeItem>(0, 0, code);
1147             method->SetCode(codeItem);
1148 
1149             CodeItem::CatchBlock cb(method, nullptr, 0);
1150             CodeItem::TryBlock tryBlock(0, 1, {cb});
1151             codeItem->AddTryBlock(tryBlock);
1152 
1153             MemoryWriter writer;
1154             ASSERT_TRUE(container.Write(&writer));
1155             auto data = writer.GetData();
1156 
1157             auto pandaFile = GetPandaFile(data);
1158             ASSERT_NE(pandaFile, nullptr);
1159 
1160             ClassDataAccessor cda(*pandaFile, File::EntityId(klass->GetOffset()));
1161             cda.EnumerateMethods([&](MethodDataAccessor &mda) {
1162                 CodeDataAccessor codeData(*pandaFile, mda.GetCodeId().value());
1163                 codeData.EnumerateTryBlocks([](const CodeDataAccessor::TryBlock &) { return true; });
1164             });
1165         },
1166         ".*");
1167 }
1168 
TEST(ItemContainer,StartPcTooBigShouldFatal)1169 TEST(ItemContainer, StartPcTooBigShouldFatal)
1170 {
1171     EXPECT_DEATH(
1172         {
1173             ItemContainer container;
1174             auto *klass = container.GetOrCreateClassItem("DeathTestKlass");
1175             auto *methodName = container.GetOrCreateStringItem("test_method");
1176             auto *proto =
1177                 container.GetOrCreateProtoItem(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID), {});
1178             auto *method =
1179                 klass->AddMethod(methodName, proto, ACC_PUBLIC | ACC_STATIC, std::vector<MethodParamItem> {});
1180 
1181             std::vector<uint8_t> instructions(4, 0);
1182             auto *codeItem = container.CreateItem<CodeItem>(0, 0, instructions);
1183             method->SetCode(codeItem);
1184 
1185             CodeItem::CatchBlock cb(method, nullptr, 0);
1186             CodeItem::TryBlock tryBlock(4, 1, std::vector {cb});
1187             codeItem->AddTryBlock(tryBlock);
1188 
1189             MemoryWriter writer;
1190             ASSERT_TRUE(container.Write(&writer));
1191             auto data = writer.GetData();
1192 
1193             auto pandaFile = GetPandaFile(data);
1194             ASSERT_NE(pandaFile, nullptr);
1195 
1196             ClassDataAccessor cda(*pandaFile, File::EntityId(klass->GetOffset()));
1197             cda.EnumerateMethods([&](MethodDataAccessor &mda) {
1198                 CodeDataAccessor codeData(*pandaFile, mda.GetCodeId().value());
1199                 codeData.EnumerateTryBlocks([&](const CodeDataAccessor::TryBlock &) { return true; });
1200             });
1201         },
1202         ".*");
1203 }
1204 
TEST(ItemContainer,LengthTooBigShouldFatal)1205 TEST(ItemContainer, LengthTooBigShouldFatal)
1206 {
1207     EXPECT_DEATH(
1208         {
1209             ItemContainer container;
1210             auto *klass = container.GetOrCreateClassItem("DeathTest_LengthTooBig");
1211             auto *methodName = container.GetOrCreateStringItem("test_method_len");
1212             auto *proto =
1213                 container.GetOrCreateProtoItem(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID), {});
1214             auto *method =
1215                 klass->AddMethod(methodName, proto, ACC_PUBLIC | ACC_STATIC, std::vector<MethodParamItem> {});
1216 
1217             std::vector<uint8_t> code(4, 0);
1218             auto *codeItem = container.CreateItem<CodeItem>(0, 0, code);
1219             method->SetCode(codeItem);
1220 
1221             CodeItem::CatchBlock cb(method, nullptr, 0);
1222             CodeItem::TryBlock tryBlock(0, 5, {cb});
1223             codeItem->AddTryBlock(tryBlock);
1224 
1225             MemoryWriter writer;
1226             ASSERT_TRUE(container.Write(&writer));
1227             auto data = writer.GetData();
1228 
1229             auto pandaFile = GetPandaFile(data);
1230             ASSERT_NE(pandaFile, nullptr);
1231 
1232             ClassDataAccessor cda(*pandaFile, File::EntityId(klass->GetOffset()));
1233             cda.EnumerateMethods([&](MethodDataAccessor &mda) {
1234                 CodeDataAccessor codeData(*pandaFile, mda.GetCodeId().value());
1235                 codeData.EnumerateTryBlocks([](const CodeDataAccessor::TryBlock &) { return true; });
1236             });
1237         },
1238         ".*");
1239 }
1240 
1241 class FakeCatchBlock : public ark::panda_file::CodeItem::CatchBlock {
1242 public:
1243     using CodeItem::CatchBlock::CatchBlock;
1244 
CalculateSize() const1245     size_t CalculateSize() const override
1246     {
1247         return std::numeric_limits<size_t>::max();
1248     }
1249 
Write(Writer * writer)1250     bool Write(Writer *writer) override
1251     {
1252         writer->Write<uint32_t>(0);
1253         return true;
1254     }
1255 
GetItemType() const1256     ItemTypes GetItemType() const override
1257     {
1258         return ItemTypes::CATCH_BLOCK_ITEM;
1259     }
1260 
IsValid() const1261     bool IsValid() const
1262     {
1263         return true;
1264     }
1265 };
1266 
TriggerFatalTryBlock()1267 void TriggerFatalTryBlock()
1268 {
1269     ItemContainer container;
1270     auto *klass = container.GetOrCreateClassItem("DeathTestFatal");
1271     auto *methodName = container.GetOrCreateStringItem("test_fatal");
1272     auto *proto = container.GetOrCreateProtoItem(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID), {});
1273     auto *method = klass->AddMethod(methodName, proto, ACC_PUBLIC | ACC_STATIC, std::vector<MethodParamItem> {});
1274 
1275     // NOLINTNEXTLINE(readability-identifier-naming)
1276     constexpr uint32_t kCodeSize = 2;
1277     std::vector<uint8_t> code(kCodeSize, 0);
1278     auto *codeItem = container.CreateItem<CodeItem>(0, 0, code);
1279     method->SetCode(codeItem);
1280 
1281     CodeItem::CatchBlock cb(method, nullptr, 0);
1282     // NOLINTNEXTLINE(readability-identifier-naming)
1283     constexpr uint32_t kTryBlockLength = 10;
1284     CodeItem::TryBlock tryBlock(0, kTryBlockLength, {cb});
1285     codeItem->AddTryBlock(tryBlock);
1286 
1287     MemoryWriter writer;
1288     ASSERT_TRUE(container.Write(&writer));
1289     auto buf = writer.GetData();
1290 
1291     auto pandaFile = GetPandaFile(buf);
1292     ASSERT_NE(pandaFile, nullptr);
1293 
1294     ClassDataAccessor cda(*pandaFile, File::EntityId(klass->GetOffset()));
1295     cda.EnumerateMethods([&](MethodDataAccessor &mda) {
1296         CodeDataAccessor codeData(*pandaFile, mda.GetCodeId().value());
1297         codeData.EnumerateTryBlocks([](const CodeDataAccessor::TryBlock &) { return true; });
1298     });
1299 }
1300 
TEST(ItemContainer,TryBlockDeclaredSizeOverflowShouldFatal)1301 TEST(ItemContainer, TryBlockDeclaredSizeOverflowShouldFatal)
1302 {
1303     EXPECT_DEATH(TriggerFatalTryBlock(), ".*");
1304 }
1305 
CreateNormalPandaFile()1306 static std::vector<uint8_t> CreateNormalPandaFile()
1307 {
1308     ark::panda_file::ItemContainer container;
1309 
1310     ark::panda_file::ClassItem *classItem = container.GetOrCreateClassItem("TestClass");
1311     classItem->SetAccessFlags(ark::ACC_PUBLIC);
1312 
1313     ark::panda_file::LiteralArrayItem *literalArray = container.GetOrCreateLiteralArrayItem("test_literal");
1314 
1315     std::vector<ark::panda_file::LiteralItem> literalItems;
1316 
1317     // NOLINTNEXTLINE(readability-magic-numbers)
1318     constexpr uint32_t LITERAL_VALUE = 42U;
1319     literalItems.emplace_back(LITERAL_VALUE);
1320     literalArray->AddItems(literalItems);
1321     ark::panda_file::MemoryWriter writer;
1322     EXPECT_TRUE(container.Write(&writer));
1323     return writer.GetData();
1324 }
1325 
CreateCorruptedPandaFile()1326 static std::vector<uint8_t> CreateCorruptedPandaFile()
1327 {
1328     auto normalData = CreateNormalPandaFile();
1329 
1330     auto *header = reinterpret_cast<ark::panda_file::File::Header *>(normalData.data());
1331     const int literalArrayOffset = 1000;
1332     header->literalarrayIdxOff = header->fileSize + literalArrayOffset;
1333     auto checksumSize = sizeof(ark::panda_file::File::Header::checksum);
1334     auto fileContentOffset = ark::panda_file::File::MAGIC_SIZE + checksumSize;
1335 
1336     // need to modify the checksum or can not pass the ValidateChecksum
1337     uint8_t *pData = &normalData[fileContentOffset];
1338     uint32_t calChecksum = adler32(1, pData, header->fileSize - fileContentOffset);
1339     header->checksum = calChecksum;
1340 
1341     return normalData;
1342 }
1343 
TEST(ItemContainer,LiteralDataAccessorTest)1344 TEST(ItemContainer, LiteralDataAccessorTest)
1345 {
1346     auto data = CreateCorruptedPandaFile();
1347     auto pandaFile = GetPandaFile(data);
1348     ASSERT_NE(pandaFile, nullptr);
1349 
1350     File::EntityId literalArraysId = pandaFile->GetLiteralArraysId();
1351     ark::Logger::InitializeStdLogging(ark::Logger::Level::FATAL,
1352                                       ark::Logger::ComponentMask().set(ark::Logger::Component::PANDAFILE));
1353     EXPECT_DEATH({ std::make_unique<ark::panda_file::LiteralDataAccessor>(*pandaFile, literalArraysId); },
1354                  ".*Literal data size is greater than file size.*|.*");
1355 }
1356 
1357 }  // namespace ark::panda_file::test
1358