• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2024 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 
151         File::Header header {};
152         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
153         std::fill(reinterpret_cast<uint8_t *>(&header), reinterpret_cast<uint8_t *>(&header) + sizeof(header), 0);
154         header.magic = File::MAGIC;
155         header.version = {0, 0, 0, 5};
156         header.fileSize = sizeof(File::Header);
157 
158         for (uint8_t b : Span<uint8_t>(reinterpret_cast<uint8_t *>(&header), sizeof(header))) {
159             writer.WriteByte(b);
160         }
161     }
162 
163     EXPECT_NE(File::Open(fileName), nullptr);
164 }
165 
GetPandaFile(std::vector<uint8_t> & data)166 static std::unique_ptr<const File> GetPandaFile(std::vector<uint8_t> &data)
167 {
168     os::mem::ConstBytePtr ptr(reinterpret_cast<std::byte *>(data.data()), data.size(),
169                               [](std::byte *, size_t) noexcept {});
170     return File::OpenFromMemory(std::move(ptr));
171 }
172 
173 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
TEST(ItemContainer,TestClasses)174 TEST(ItemContainer, TestClasses)
175 {
176     // Write panda file to memory
177 
178     ItemContainer container;
179 
180     ClassItem *emptyClassItem = container.GetOrCreateClassItem("Foo");
181 
182     ClassItem *classItem = container.GetOrCreateClassItem("Bar");
183     classItem->SetAccessFlags(ACC_PUBLIC);
184     classItem->SetSuperClass(emptyClassItem);
185 
186     // Add interface
187 
188     ClassItem *ifaceItem = container.GetOrCreateClassItem("Iface");
189     ifaceItem->SetAccessFlags(ACC_PUBLIC);
190 
191     classItem->AddInterface(ifaceItem);
192 
193     // Add method
194 
195     StringItem *methodName = container.GetOrCreateStringItem("foo");
196 
197     PrimitiveTypeItem *retType = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
198     std::vector<MethodParamItem> params;
199     ProtoItem *protoItem = container.GetOrCreateProtoItem(retType, params);
200 
201     MethodItem *methodItem = classItem->AddMethod(methodName, protoItem, ACC_PUBLIC | ACC_STATIC, params);
202 
203     // Add field
204 
205     StringItem *fieldName = container.GetOrCreateStringItem("field");
206     PrimitiveTypeItem *fieldType = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32);
207 
208     FieldItem *fieldItem = classItem->AddField(fieldName, fieldType, ACC_PUBLIC);
209 
210     // Add runtime annotation
211 
212     std::vector<AnnotationItem::Elem> runtimeElems;
213     std::vector<AnnotationItem::Tag> runtimeTags;
214     auto *runtimeAnnotationItem = container.CreateItem<AnnotationItem>(classItem, runtimeElems, runtimeTags);
215 
216     classItem->AddRuntimeAnnotation(runtimeAnnotationItem);
217 
218     // Add annotation
219 
220     std::vector<AnnotationItem::Elem> elems;
221     std::vector<AnnotationItem::Tag> tags;
222     auto *annotationItem = container.CreateItem<AnnotationItem>(classItem, elems, tags);
223 
224     classItem->AddAnnotation(annotationItem);
225 
226     // Add source file
227 
228     StringItem *sourceFile = container.GetOrCreateStringItem("source_file");
229 
230     classItem->SetSourceFile(sourceFile);
231 
232     MemoryWriter memWriter;
233 
234     ASSERT_TRUE(container.Write(&memWriter));
235 
236     // Read panda file from memory
237 
238     auto data = memWriter.GetData();
239     auto pandaFile = GetPandaFile(data);
240 
241     ASSERT_NE(pandaFile, nullptr);
242 
243     EXPECT_THAT(pandaFile->GetHeader()->version, ::testing::ElementsAre(0, 0, 0, 5));
244     EXPECT_EQ(pandaFile->GetHeader()->fileSize, memWriter.GetData().size());
245     EXPECT_EQ(pandaFile->GetHeader()->foreignOff, 0U);
246     EXPECT_EQ(pandaFile->GetHeader()->foreignSize, 0U);
247     EXPECT_EQ(pandaFile->GetHeader()->numClasses, 3U);
248     EXPECT_EQ(pandaFile->GetHeader()->classIdxOff, sizeof(File::Header));
249 
250     // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
251     const auto *classIndex =
252         reinterpret_cast<const uint32_t *>(pandaFile->GetBase() + pandaFile->GetHeader()->classIdxOff);
253     EXPECT_EQ(classIndex[0], classItem->GetOffset());
254     EXPECT_EQ(classIndex[1], emptyClassItem->GetOffset());
255     // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
256 
257     std::vector<uint8_t> className {'B', 'a', 'r', '\0'};
258     auto classId = pandaFile->GetClassId(className.data());
259     EXPECT_EQ(classId.GetOffset(), classItem->GetOffset());
260 
261     ClassDataAccessor classDataAccessor(*pandaFile, classId);
262     EXPECT_EQ(classDataAccessor.GetSuperClassId().GetOffset(), emptyClassItem->GetOffset());
263     EXPECT_EQ(classDataAccessor.GetAccessFlags(), ACC_PUBLIC);
264     EXPECT_EQ(classDataAccessor.GetFieldsNumber(), 1U);
265     EXPECT_EQ(classDataAccessor.GetMethodsNumber(), 1U);
266     EXPECT_EQ(classDataAccessor.GetIfacesNumber(), 1U);
267     EXPECT_TRUE(classDataAccessor.GetSourceFileId().has_value());
268     EXPECT_EQ(classDataAccessor.GetSourceFileId().value().GetOffset(), sourceFile->GetOffset());
269     EXPECT_EQ(classDataAccessor.GetSize(), classItem->GetSize());
270 
271     classDataAccessor.EnumerateInterfaces([&](File::EntityId id) {
272         EXPECT_EQ(id.GetOffset(), ifaceItem->GetOffset());
273 
274         ClassDataAccessor ifaceClassDataAccessor(*pandaFile, id);
275         EXPECT_EQ(ifaceClassDataAccessor.GetSuperClassId().GetOffset(), 0U);
276         EXPECT_EQ(ifaceClassDataAccessor.GetAccessFlags(), ACC_PUBLIC);
277         EXPECT_EQ(ifaceClassDataAccessor.GetFieldsNumber(), 0U);
278         EXPECT_EQ(ifaceClassDataAccessor.GetMethodsNumber(), 0U);
279         EXPECT_EQ(ifaceClassDataAccessor.GetIfacesNumber(), 0U);
280         EXPECT_FALSE(ifaceClassDataAccessor.GetSourceFileId().has_value());
281         EXPECT_EQ(ifaceClassDataAccessor.GetSize(), ifaceItem->GetSize());
282     });
283 
284     classDataAccessor.EnumerateRuntimeAnnotations([&](File::EntityId id) {
285         EXPECT_EQ(id.GetOffset(), runtimeAnnotationItem->GetOffset());
286 
287         AnnotationDataAccessor dataAccessor(*pandaFile, id);
288         EXPECT_EQ(dataAccessor.GetAnnotationId().GetOffset(), runtimeAnnotationItem->GetOffset());
289         EXPECT_EQ(dataAccessor.GetClassId().GetOffset(), classItem->GetOffset());
290         EXPECT_EQ(dataAccessor.GetCount(), 0U);
291     });
292 
293     // Annotation is the same as the runtime one, so we deduplicate it
294     EXPECT_FALSE(annotationItem->NeedsEmit());
295     annotationItem = runtimeAnnotationItem;
296 
297     classDataAccessor.EnumerateAnnotations([&](File::EntityId id) {
298         EXPECT_EQ(id.GetOffset(), annotationItem->GetOffset());
299 
300         AnnotationDataAccessor dataAccessor(*pandaFile, id);
301         EXPECT_EQ(dataAccessor.GetAnnotationId().GetOffset(), annotationItem->GetOffset());
302         EXPECT_EQ(dataAccessor.GetClassId().GetOffset(), classItem->GetOffset());
303         EXPECT_EQ(dataAccessor.GetCount(), 0U);
304     });
305 
306     classDataAccessor.EnumerateFields([&](FieldDataAccessor &dataAccessor) {
307         EXPECT_EQ(dataAccessor.GetFieldId().GetOffset(), fieldItem->GetOffset());
308         EXPECT_EQ(dataAccessor.GetClassId().GetOffset(), classItem->GetOffset());
309         EXPECT_EQ(dataAccessor.GetNameId().GetOffset(), fieldName->GetOffset());
310         EXPECT_EQ(dataAccessor.GetType(), Type(Type::TypeId::I32).GetFieldEncoding());
311         EXPECT_EQ(dataAccessor.GetAccessFlags(), ACC_PUBLIC);
312         EXPECT_FALSE(dataAccessor.GetValue<int32_t>().has_value());
313         EXPECT_EQ(dataAccessor.GetSize(), fieldItem->GetSize());
314 
315         dataAccessor.EnumerateRuntimeAnnotations([](File::EntityId) { EXPECT_TRUE(false); });
316         dataAccessor.EnumerateAnnotations([](File::EntityId) { EXPECT_TRUE(false); });
317     });
318 
319     classDataAccessor.EnumerateMethods([&](MethodDataAccessor &dataAccessor) {
320         EXPECT_FALSE(dataAccessor.IsExternal());
321         EXPECT_EQ(dataAccessor.GetMethodId().GetOffset(), methodItem->GetOffset());
322         EXPECT_EQ(dataAccessor.GetClassId().GetOffset(), classItem->GetOffset());
323         EXPECT_EQ(dataAccessor.GetNameId().GetOffset(), methodName->GetOffset());
324         EXPECT_EQ(dataAccessor.GetProtoId().GetOffset(), protoItem->GetOffset());
325         EXPECT_EQ(dataAccessor.GetAccessFlags(), ACC_PUBLIC | ACC_STATIC);
326         EXPECT_FALSE(dataAccessor.GetCodeId().has_value());
327         EXPECT_EQ(dataAccessor.GetSize(), methodItem->GetSize());
328         EXPECT_FALSE(dataAccessor.GetRuntimeParamAnnotationId().has_value());
329         EXPECT_FALSE(dataAccessor.GetParamAnnotationId().has_value());
330         EXPECT_FALSE(dataAccessor.GetDebugInfoId().has_value());
331 
332         dataAccessor.EnumerateRuntimeAnnotations([](File::EntityId) { EXPECT_TRUE(false); });
333         dataAccessor.EnumerateAnnotations([](File::EntityId) { EXPECT_TRUE(false); });
334     });
335 
336     ClassDataAccessor emptyClassDataAccessor(*pandaFile, File::EntityId(emptyClassItem->GetOffset()));
337     EXPECT_EQ(emptyClassDataAccessor.GetSuperClassId().GetOffset(), 0U);
338     EXPECT_EQ(emptyClassDataAccessor.GetAccessFlags(), 0U);
339     EXPECT_EQ(emptyClassDataAccessor.GetFieldsNumber(), 0U);
340     EXPECT_EQ(emptyClassDataAccessor.GetMethodsNumber(), 0U);
341     EXPECT_EQ(emptyClassDataAccessor.GetIfacesNumber(), 0U);
342     EXPECT_FALSE(emptyClassDataAccessor.GetSourceFileId().has_value());
343     EXPECT_EQ(emptyClassDataAccessor.GetSize(), emptyClassItem->GetSize());
344 }
345 
TEST(ItemContainer,TestMethods)346 TEST(ItemContainer, TestMethods)
347 {
348     // Write panda file to memory
349 
350     ItemContainer container;
351 
352     ClassItem *classItem = container.GetOrCreateClassItem("A");
353     classItem->SetAccessFlags(ACC_PUBLIC);
354 
355     StringItem *methodName = container.GetOrCreateStringItem("foo");
356 
357     PrimitiveTypeItem *retType = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
358     std::vector<MethodParamItem> params;
359     ProtoItem *protoItem = container.GetOrCreateProtoItem(retType, params);
360 
361     MethodItem *methodItem = classItem->AddMethod(methodName, protoItem, ACC_PUBLIC | ACC_STATIC, params);
362 
363     std::vector<uint8_t> instructions {1, 2, 3, 4};
364     auto *codeItem = container.CreateItem<CodeItem>(0, 2, instructions);
365 
366     methodItem->SetCode(codeItem);
367 
368     MemoryWriter memWriter;
369 
370     ASSERT_TRUE(container.Write(&memWriter));
371 
372     // Read panda file from memory
373 
374     auto data = memWriter.GetData();
375     auto pandaFile = GetPandaFile(data);
376 
377     ASSERT_NE(pandaFile, nullptr);
378 
379     ClassDataAccessor classDataAccessor(*pandaFile, File::EntityId(classItem->GetOffset()));
380 
381     classDataAccessor.EnumerateMethods([&](MethodDataAccessor &dataAccessor) {
382         EXPECT_FALSE(dataAccessor.IsExternal());
383         EXPECT_EQ(dataAccessor.GetMethodId().GetOffset(), methodItem->GetOffset());
384         EXPECT_EQ(dataAccessor.GetClassId().GetOffset(), classItem->GetOffset());
385         EXPECT_EQ(dataAccessor.GetNameId().GetOffset(), methodName->GetOffset());
386         EXPECT_EQ(dataAccessor.GetProtoId().GetOffset(), protoItem->GetOffset());
387         EXPECT_EQ(dataAccessor.GetAccessFlags(), ACC_PUBLIC | ACC_STATIC);
388         EXPECT_EQ(dataAccessor.GetSize(), methodItem->GetSize());
389 
390         auto codeId = dataAccessor.GetCodeId();
391         EXPECT_TRUE(codeId.has_value());
392         EXPECT_EQ(codeId.value().GetOffset(), codeItem->GetOffset());
393 
394         CodeDataAccessor codeDataAccessor(*pandaFile, codeId.value());
395         EXPECT_EQ(codeDataAccessor.GetNumVregs(), 0U);
396         EXPECT_EQ(codeDataAccessor.GetNumArgs(), 2U);
397         EXPECT_EQ(codeDataAccessor.GetCodeSize(), instructions.size());
398         EXPECT_THAT(instructions,
399                     ::testing::ElementsAreArray(codeDataAccessor.GetInstructions(), codeDataAccessor.GetCodeSize()));
400 
401         EXPECT_EQ(codeDataAccessor.GetTriesSize(), 0U);
402         EXPECT_EQ(codeDataAccessor.GetSize(), codeItem->GetSize());
403 
404         codeDataAccessor.EnumerateTryBlocks([](const CodeDataAccessor::TryBlock &) {
405             EXPECT_TRUE(false);
406             return false;
407         });
408 
409         EXPECT_FALSE(dataAccessor.GetDebugInfoId().has_value());
410 
411         EXPECT_FALSE(dataAccessor.GetRuntimeParamAnnotationId().has_value());
412 
413         EXPECT_FALSE(dataAccessor.GetParamAnnotationId().has_value());
414 
415         dataAccessor.EnumerateRuntimeAnnotations([](File::EntityId) { EXPECT_TRUE(false); });
416 
417         dataAccessor.EnumerateAnnotations([](File::EntityId) { EXPECT_TRUE(false); });
418     });
419 }
420 
421 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
TestProtos(size_t n)422 void TestProtos(size_t n)
423 {
424     constexpr size_t ELEM_WIDTH = 4;
425     constexpr size_t ELEM_PER16 = 16 / ELEM_WIDTH;
426 
427     // Write panda file to memory
428 
429     ItemContainer container;
430 
431     ClassItem *classItem = container.GetOrCreateClassItem("A");
432     classItem->SetAccessFlags(ACC_PUBLIC);
433 
434     StringItem *methodName = container.GetOrCreateStringItem("foo");
435 
436     std::vector<Type::TypeId> types {Type::TypeId::VOID, Type::TypeId::I32};
437     std::vector<ClassItem *> refTypes;
438 
439     PrimitiveTypeItem *retType = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
440     std::vector<MethodParamItem> params;
441 
442     params.emplace_back(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32));
443 
444     for (size_t i = 0; i < ELEM_PER16 * 2U - 2U; i++) {
445         params.emplace_back(container.GetOrCreateClassItem("B"));
446         types.push_back(Type::TypeId::REFERENCE);
447         refTypes.push_back(container.GetOrCreateClassItem("B"));
448         params.emplace_back(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::F64));
449         types.push_back(Type::TypeId::F64);
450     }
451 
452     for (size_t i = 0; i < n; i++) {
453         params.emplace_back(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::F32));
454         types.push_back(Type::TypeId::F32);
455     }
456 
457     ProtoItem *protoItem = container.GetOrCreateProtoItem(retType, params);
458 
459     MethodItem *methodItem = classItem->AddMethod(methodName, protoItem, ACC_PUBLIC | ACC_STATIC, params);
460 
461     MemoryWriter memWriter;
462 
463     ASSERT_TRUE(container.Write(&memWriter));
464 
465     // Read panda file from memory
466 
467     auto data = memWriter.GetData();
468     auto pandaFile = GetPandaFile(data);
469 
470     ASSERT_NE(pandaFile, nullptr);
471 
472     ClassDataAccessor classDataAccessor(*pandaFile, File::EntityId(classItem->GetOffset()));
473 
474     classDataAccessor.EnumerateMethods([&](MethodDataAccessor &dataAccessor) {
475         EXPECT_EQ(dataAccessor.GetMethodId().GetOffset(), methodItem->GetOffset());
476         EXPECT_EQ(dataAccessor.GetProtoId().GetOffset(), protoItem->GetOffset());
477 
478         ProtoDataAccessor protoDataAccessor(*pandaFile, dataAccessor.GetProtoId());
479         EXPECT_EQ(protoDataAccessor.GetProtoId().GetOffset(), protoItem->GetOffset());
480 
481         size_t num = 0;
482         size_t nref = 0;
483         protoDataAccessor.EnumerateTypes([&](Type t) {
484             EXPECT_EQ(t.GetEncoding(), Type(types[num]).GetEncoding());
485             ++num;
486 
487             if (!t.IsPrimitive()) {
488                 ++nref;
489             }
490         });
491 
492         EXPECT_EQ(num, types.size());
493 
494         for (size_t i = 0; i < num - 1; i++) {
495             EXPECT_EQ(protoDataAccessor.GetArgType(i).GetEncoding(), Type(types[i + 1]).GetEncoding());
496         }
497 
498         EXPECT_EQ(protoDataAccessor.GetReturnType().GetEncoding(), Type(types[0]).GetEncoding());
499 
500         EXPECT_EQ(nref, refTypes.size());
501 
502         for (size_t i = 0; i < nref; i++) {
503             EXPECT_EQ(protoDataAccessor.GetReferenceType(0).GetOffset(), refTypes[i]->GetOffset());
504         }
505 
506         size_t size = ((num + ELEM_PER16) / ELEM_PER16 + nref) * sizeof(uint16_t);
507 
508         EXPECT_EQ(protoDataAccessor.GetSize(), size);
509         EXPECT_EQ(protoDataAccessor.GetSize(), protoItem->GetSize());
510     });
511 }
512 
TEST(ItemContainer,TestProtos)513 TEST(ItemContainer, TestProtos)
514 {
515     TestProtos(0);
516     TestProtos(1);
517     TestProtos(2);
518     TestProtos(7);
519 }
520 
521 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
TEST(ItemContainer,TestDebugInfo)522 TEST(ItemContainer, TestDebugInfo)
523 {
524     // Write panda file to memory
525 
526     ItemContainer container;
527 
528     ClassItem *classItem = container.GetOrCreateClassItem("A");
529     classItem->SetAccessFlags(ACC_PUBLIC);
530 
531     StringItem *methodName = container.GetOrCreateStringItem("foo");
532 
533     PrimitiveTypeItem *retType = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
534     std::vector<MethodParamItem> params;
535     params.emplace_back(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32));
536     ProtoItem *protoItem = container.GetOrCreateProtoItem(retType, params);
537     MethodItem *methodItem = classItem->AddMethod(methodName, protoItem, ACC_PUBLIC | ACC_STATIC, params);
538 
539     StringItem *sourceFileItem = container.GetOrCreateStringItem("<source>");
540     StringItem *sourceCodeItem = container.GetOrCreateStringItem("let a = 1;");
541     StringItem *paramStringItem = container.GetOrCreateStringItem("a0");
542 
543     LineNumberProgramItem *lineNumberProgramItem = container.CreateLineNumberProgramItem();
544     auto *debugInfoItem = container.CreateItem<DebugInfoItem>(lineNumberProgramItem);
545     methodItem->SetDebugInfo(debugInfoItem);
546 
547     // Add debug info
548 
549     container.ComputeLayout();
550 
551     std::vector<uint8_t> opcodes {
552         static_cast<uint8_t>(LineNumberProgramItem::Opcode::SET_SOURCE_CODE),
553         static_cast<uint8_t>(LineNumberProgramItem::Opcode::SET_FILE),
554         static_cast<uint8_t>(LineNumberProgramItem::Opcode::SET_PROLOGUE_END),
555         static_cast<uint8_t>(LineNumberProgramItem::Opcode::ADVANCE_PC),
556         static_cast<uint8_t>(LineNumberProgramItem::Opcode::ADVANCE_LINE),
557         static_cast<uint8_t>(LineNumberProgramItem::Opcode::SET_EPILOGUE_BEGIN),
558         static_cast<uint8_t>(LineNumberProgramItem::Opcode::END_SEQUENCE),
559     };
560 
561     auto *constantPool = debugInfoItem->GetConstantPool();
562     debugInfoItem->SetLineNumber(5);
563     lineNumberProgramItem->EmitSetSourceCode(constantPool, sourceCodeItem);
564     lineNumberProgramItem->EmitSetFile(constantPool, sourceFileItem);
565     lineNumberProgramItem->EmitPrologueEnd();
566     // NOLINTNEXTLINE(readability-magic-numbers)
567     lineNumberProgramItem->EmitAdvancePc(constantPool, 10);
568     lineNumberProgramItem->EmitAdvanceLine(constantPool, -5);
569     lineNumberProgramItem->EmitEpilogueBegin();
570     lineNumberProgramItem->EmitEnd();
571 
572     debugInfoItem->AddParameter(paramStringItem);
573 
574     methodItem->SetDebugInfo(debugInfoItem);
575 
576     MemoryWriter memWriter;
577 
578     ASSERT_TRUE(container.Write(&memWriter));
579 
580     // Read panda file from memory
581 
582     auto data = memWriter.GetData();
583     auto pandaFile = GetPandaFile(data);
584 
585     ASSERT_NE(pandaFile, nullptr);
586 
587     ClassDataAccessor classDataAccessor(*pandaFile, File::EntityId(classItem->GetOffset()));
588 
589     classDataAccessor.EnumerateMethods([&](MethodDataAccessor &dataAccessor) {
590         EXPECT_EQ(dataAccessor.GetMethodId().GetOffset(), methodItem->GetOffset());
591         EXPECT_EQ(dataAccessor.GetSize(), methodItem->GetSize());
592 
593         auto debugInfoId = dataAccessor.GetDebugInfoId();
594         EXPECT_TRUE(debugInfoId.has_value());
595 
596         EXPECT_EQ(debugInfoId.value().GetOffset(), debugInfoItem->GetOffset());
597 
598         DebugInfoDataAccessor dda(*pandaFile, debugInfoId.value());
599         EXPECT_EQ(dda.GetDebugInfoId().GetOffset(), debugInfoItem->GetOffset());
600         EXPECT_EQ(dda.GetLineStart(), 5U);
601         EXPECT_EQ(dda.GetNumParams(), params.size());
602 
603         dda.EnumerateParameters([&](File::EntityId id) { EXPECT_EQ(id.GetOffset(), paramStringItem->GetOffset()); });
604 
605         auto cp = dda.GetConstantPool();
606         EXPECT_EQ(cp.size(), constantPool->size());
607         EXPECT_THAT(*constantPool, ::testing::ElementsAreArray(cp.data(), cp.Size()));
608 
609         EXPECT_EQ(helpers::ReadULeb128(&cp), sourceCodeItem->GetOffset());
610         EXPECT_EQ(helpers::ReadULeb128(&cp), sourceFileItem->GetOffset());
611         EXPECT_EQ(helpers::ReadULeb128(&cp), 10U);
612         EXPECT_EQ(helpers::ReadLeb128(&cp), -5);
613 
614         const uint8_t *lineNumberProgram = dda.GetLineNumberProgram();
615         EXPECT_EQ(pandaFile->GetIdFromPointer(lineNumberProgram).GetOffset(), lineNumberProgramItem->GetOffset());
616         EXPECT_EQ(lineNumberProgramItem->GetSize(), opcodes.size());
617 
618         EXPECT_THAT(opcodes, ::testing::ElementsAreArray(lineNumberProgram, opcodes.size()));
619 
620         EXPECT_EQ(dda.GetSize(), debugInfoItem->GetSize());
621     });
622 }
623 
TEST(ItemContainer,ForeignItems)624 TEST(ItemContainer, ForeignItems)
625 {
626     ItemContainer container;
627 
628     // Create foreign class
629     ForeignClassItem *classItem = container.GetOrCreateForeignClassItem("ForeignClass");
630 
631     // Create foreign field
632     StringItem *fieldName = container.GetOrCreateStringItem("foreign_field");
633     PrimitiveTypeItem *fieldType = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32);
634     auto *fieldItem = container.CreateItem<ForeignFieldItem>(classItem, fieldName, fieldType);
635 
636     // Create foreign method
637     StringItem *methodName = container.GetOrCreateStringItem("ForeignMethod");
638     PrimitiveTypeItem *retType = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
639     std::vector<MethodParamItem> params;
640     params.emplace_back(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32));
641     ProtoItem *protoItem = container.GetOrCreateProtoItem(retType, params);
642     auto *methodItem = container.CreateItem<ForeignMethodItem>(classItem, methodName, protoItem, 0);
643 
644     MemoryWriter memWriter;
645 
646     ASSERT_TRUE(container.Write(&memWriter));
647 
648     // Read panda file from memory
649 
650     auto data = memWriter.GetData();
651     auto pandaFile = GetPandaFile(data);
652 
653     ASSERT_NE(pandaFile, nullptr);
654 
655     EXPECT_EQ(pandaFile->GetHeader()->foreignOff, classItem->GetOffset());
656 
657     size_t foreignSize = classItem->GetSize() + fieldItem->GetSize() + methodItem->GetSize();
658     EXPECT_EQ(pandaFile->GetHeader()->foreignSize, foreignSize);
659 
660     ASSERT_TRUE(pandaFile->IsExternal(classItem->GetFileId()));
661 
662     MethodDataAccessor methodDataAccessor(*pandaFile, methodItem->GetFileId());
663     EXPECT_EQ(methodDataAccessor.GetMethodId().GetOffset(), methodItem->GetOffset());
664     EXPECT_EQ(methodDataAccessor.GetSize(), methodItem->GetSize());
665     EXPECT_EQ(methodDataAccessor.GetClassId().GetOffset(), classItem->GetOffset());
666     EXPECT_EQ(methodDataAccessor.GetNameId().GetOffset(), methodName->GetOffset());
667     EXPECT_EQ(methodDataAccessor.GetProtoId().GetOffset(), protoItem->GetOffset());
668     EXPECT_TRUE(methodDataAccessor.IsExternal());
669 
670     FieldDataAccessor fieldDataAccessor(*pandaFile, fieldItem->GetFileId());
671     EXPECT_EQ(fieldDataAccessor.GetFieldId().GetOffset(), fieldItem->GetOffset());
672     EXPECT_EQ(fieldDataAccessor.GetSize(), fieldItem->GetSize());
673     EXPECT_EQ(fieldDataAccessor.GetClassId().GetOffset(), classItem->GetOffset());
674     EXPECT_EQ(fieldDataAccessor.GetNameId().GetOffset(), fieldName->GetOffset());
675     EXPECT_EQ(fieldDataAccessor.GetType(), fieldType->GetType().GetFieldEncoding());
676     EXPECT_TRUE(fieldDataAccessor.IsExternal());
677 }
678 
TEST(ItemContainer,EmptyContainerChecksum)679 TEST(ItemContainer, EmptyContainerChecksum)
680 {
681     using ark::os::file::Mode;
682     using ark::os::file::Open;
683 
684     // Write panda file to disk
685     ItemContainer container;
686 
687     const std::string fileName = "test_empty_checksum.ark";
688     auto writer = FileWriter(fileName);
689 
690     // Initial value of adler32
691     EXPECT_EQ(writer.GetChecksum(), 1);
692     ASSERT_TRUE(container.Write(&writer));
693 
694     // At least header was written so the checksum should be changed
695     auto containerChecksum = writer.GetChecksum();
696     EXPECT_NE(containerChecksum, 1);
697 
698     // Read panda file from disk
699     auto file = File::Open(fileName);
700     EXPECT_NE(file, nullptr);
701     EXPECT_EQ(file->GetHeader()->checksum, containerChecksum);
702 
703     constexpr size_t DATA_OFFSET = 12U;
704     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
705     auto checksum = adler32(1, file->GetBase() + DATA_OFFSET, file->GetHeader()->fileSize - DATA_OFFSET);
706     EXPECT_EQ(file->GetHeader()->checksum, checksum);
707 }
708 
TEST(ItemContainer,ContainerChecksum)709 TEST(ItemContainer, ContainerChecksum)
710 {
711     using ark::os::file::Mode;
712     using ark::os::file::Open;
713 
714     uint32_t emptyChecksum = 0;
715     {
716         ItemContainer container;
717         const std::string fileName = "test_checksum_empty.ark";
718         auto writer = FileWriter(fileName);
719         ASSERT_TRUE(container.Write(&writer));
720         emptyChecksum = writer.GetChecksum();
721     }
722     ASSERT(emptyChecksum != 0);
723 
724     // Create not empty container
725     ItemContainer container;
726     container.GetOrCreateClassItem("C");
727 
728     const std::string fileName = "test_checksum.ark";
729     auto writer = FileWriter(fileName);
730 
731     ASSERT_TRUE(container.Write(&writer));
732 
733     // This checksum must be different from the empty one (collision may happen though)
734     auto containerChecksum = writer.GetChecksum();
735     EXPECT_NE(emptyChecksum, containerChecksum);
736 
737     // Read panda file from disk
738     auto file = File::Open(fileName);
739     EXPECT_NE(file, nullptr);
740     EXPECT_EQ(file->GetHeader()->checksum, containerChecksum);
741 
742     constexpr size_t DATA_OFFSET = 12U;
743     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
744     auto checksum = adler32(1, file->GetBase() + DATA_OFFSET, file->GetHeader()->fileSize - DATA_OFFSET);
745     EXPECT_EQ(file->GetHeader()->checksum, checksum);
746 }
747 
748 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
TEST(ItemContainer,TestProfileGuidedRelayout)749 TEST(ItemContainer, TestProfileGuidedRelayout)
750 {
751     ItemContainer container;
752 
753     // Add classes
754     ClassItem *emptyClassItem = container.GetOrCreateClassItem("LTest;");
755     ClassItem *classItemA = container.GetOrCreateClassItem("LAA;");
756     classItemA->SetSuperClass(emptyClassItem);
757     ClassItem *classItemB = container.GetOrCreateClassItem("LBB;");
758 
759     // Add method1
760     StringItem *methodName1 = container.GetOrCreateStringItem("foo1");
761     PrimitiveTypeItem *retType1 = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
762     std::vector<MethodParamItem> params1;
763     ProtoItem *protoItem1 = container.GetOrCreateProtoItem(retType1, params1);
764     MethodItem *methodItem1 = classItemA->AddMethod(methodName1, protoItem1, ACC_PUBLIC | ACC_STATIC, params1);
765     // Set code_1
766     std::vector<uint8_t> instructions1 {1, 2, 3, 4};
767     auto *codeItem1 = container.CreateItem<CodeItem>(0, 2, instructions1);
768     methodItem1->SetCode(codeItem1);
769     codeItem1->AddMethod(methodItem1);
770 
771     // Add method2
772     StringItem *methodName2 = container.GetOrCreateStringItem("foo2");
773     PrimitiveTypeItem *retType2 = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32);
774     std::vector<MethodParamItem> params2;
775     ProtoItem *protoItem2 = container.GetOrCreateProtoItem(retType2, params2);
776     MethodItem *methodItem2 = classItemB->AddMethod(methodName2, protoItem2, ACC_PUBLIC | ACC_STATIC, params2);
777     // Set code_2
778     std::vector<uint8_t> instructions2 {5, 6, 7, 8};
779     auto *codeItem2 = container.CreateItem<CodeItem>(0, 2, instructions2);
780     methodItem2->SetCode(codeItem2);
781     codeItem2->AddMethod(methodItem2);
782 
783     // Add method_3
784     StringItem *methodName3 = container.GetOrCreateStringItem("foo3");
785     auto *methodItem3 = emptyClassItem->AddMethod(methodName3, protoItem1, ACC_PUBLIC | ACC_STATIC, params1);
786     // Set code_3
787     std::vector<uint8_t> instructions3 {3, 4, 5, 6};
788     auto *codeItem3 = container.CreateItem<CodeItem>(0, 2, instructions3);
789     methodItem3->SetCode(codeItem3);
790     codeItem3->AddMethod(methodItem3);
791 
792     // Add method_4
793     StringItem *methodName4 = container.GetOrCreateStringItem("foo4");
794     auto *methodItem4 = emptyClassItem->AddMethod(methodName4, protoItem1, ACC_PUBLIC | ACC_STATIC, params1);
795     // Set code. method_4 and method_3 share code_item_3
796     methodItem4->SetCode(codeItem3);
797     codeItem3->AddMethod(methodItem4);
798 
799     // Add field
800     StringItem *fieldName = container.GetOrCreateStringItem("test_field");
801     PrimitiveTypeItem *fieldType = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32);
802     classItemA->AddField(fieldName, fieldType, ACC_PUBLIC);
803 
804     // Add source file
805     StringItem *sourceFile = container.GetOrCreateStringItem("source_file");
806     classItemA->SetSourceFile(sourceFile);
807 
808     constexpr std::string_view PRIMITIVE_TYPE_ITEM = "primitive_type_item";
809     constexpr std::string_view PROTO_ITEM = "proto_item";
810     constexpr std::string_view END_ITEM = "end_item";
811 
812     // Items before PGO
813     const auto &items = container.GetItems();
814     auto item = items.begin();
815     EXPECT_EQ((*item)->GetName(), CLASS_ITEM);
816     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "Test");
817     item++;
818     EXPECT_EQ((*item)->GetName(), CLASS_ITEM);
819     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "AA");
820     item++;
821     EXPECT_EQ((*item)->GetName(), CLASS_ITEM);
822     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "BB");
823     item++;
824     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
825     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo1");
826     item++;
827     EXPECT_EQ((*item)->GetName(), PRIMITIVE_TYPE_ITEM);
828     item++;
829     EXPECT_EQ((*item)->GetName(), PROTO_ITEM);
830     item++;
831     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
832     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo2");
833     item++;
834     EXPECT_EQ((*item)->GetName(), PRIMITIVE_TYPE_ITEM);
835     item++;
836     EXPECT_EQ((*item)->GetName(), PROTO_ITEM);
837     item++;
838     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
839     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo3");
840     item++;
841     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
842     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo4");
843     item++;
844     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
845     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "test_field");
846     item++;
847     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
848     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "source_file");
849     item++;
850     EXPECT_EQ((*item)->GetName(), END_ITEM);
851     item++;
852     EXPECT_EQ((*item)->GetName(), END_ITEM);
853     item++;
854     EXPECT_EQ((*item)->GetName(), CODE_ITEM);
855     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[0], "AA::foo1");
856     item++;
857     EXPECT_EQ((*item)->GetName(), CODE_ITEM);
858     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[0], "BB::foo2");
859     item++;
860     EXPECT_EQ((*item)->GetName(), CODE_ITEM);
861     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[0], "Test::foo3");
862     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[1], "Test::foo4");
863     item++;
864     EXPECT_EQ((*item)->GetName(), END_ITEM);
865     item++;
866     EXPECT_EQ((*item)->GetName(), END_ITEM);
867     item++;
868     EXPECT_EQ(item, items.end());
869 
870     // Prepare profile data
871     std::string profilePath = "TestProfileGuidedRelayout_profile_test_data.txt";
872     std::ofstream testFile;
873     testFile.open(profilePath);
874     testFile << "string_item:test_field" << std::endl;
875     testFile << "class_item:BB" << std::endl;
876     testFile << "code_item:BB::foo2" << std::endl;
877     testFile << "code_item:Test::foo4" << std::endl;
878     testFile.close();
879 
880     // Run PGO
881     ark::panda_file::pgo::ProfileOptimizer profileOpt;
882     profileOpt.SetProfilePath(profilePath);
883     container.ReorderItems(&profileOpt);
884 
885     // Items after PGO
886     item = items.begin();
887     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
888     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "test_field");
889     item++;
890     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
891     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo1");
892     item++;
893     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
894     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo2");
895     item++;
896     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
897     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo3");
898     item++;
899     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
900     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo4");
901     item++;
902     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
903     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "source_file");
904     item++;
905     EXPECT_EQ((*item)->GetName(), CLASS_ITEM);
906     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "BB");
907     item++;
908     EXPECT_EQ((*item)->GetName(), CLASS_ITEM);
909     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "Test");
910     item++;
911     EXPECT_EQ((*item)->GetName(), CLASS_ITEM);
912     EXPECT_EQ(ark::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "AA");
913     item++;
914     EXPECT_EQ((*item)->GetName(), PRIMITIVE_TYPE_ITEM);
915     item++;
916     EXPECT_EQ((*item)->GetName(), PROTO_ITEM);
917     item++;
918     EXPECT_EQ((*item)->GetName(), PRIMITIVE_TYPE_ITEM);
919     item++;
920     EXPECT_EQ((*item)->GetName(), PROTO_ITEM);
921     item++;
922     EXPECT_EQ((*item)->GetName(), END_ITEM);
923     item++;
924     EXPECT_EQ((*item)->GetName(), END_ITEM);
925     item++;
926     EXPECT_EQ((*item)->GetName(), END_ITEM);
927     item++;
928     EXPECT_EQ((*item)->GetName(), END_ITEM);
929     item++;
930     EXPECT_EQ((*item)->GetName(), CODE_ITEM);
931     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[0], "BB::foo2");
932     item++;
933     EXPECT_EQ((*item)->GetName(), CODE_ITEM);
934     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[0], "Test::foo3");
935     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[1], "Test::foo4");
936     item++;
937     EXPECT_EQ((*item)->GetName(), CODE_ITEM);
938     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[0], "AA::foo1");
939     item++;
940     EXPECT_EQ(item, items.end());
941 }
942 
943 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
TEST(ItemContainer,GettersTest)944 TEST(ItemContainer, GettersTest)
945 {
946     ItemContainer container;
947 
948     ClassItem *emptyClassItem = container.GetOrCreateClassItem("Foo");
949 
950     ClassItem *classItem = container.GetOrCreateClassItem("Bar");
951     classItem->SetAccessFlags(ACC_PUBLIC);
952     classItem->SetSuperClass(emptyClassItem);
953 
954     // Add methods
955 
956     StringItem *methodName1 = container.GetOrCreateStringItem("foo1");
957 
958     PrimitiveTypeItem *retType1 = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
959     std::vector<MethodParamItem> params1;
960     params1.emplace_back(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32));
961     ProtoItem *protoItem1 = container.GetOrCreateProtoItem(retType1, params1);
962 
963     classItem->AddMethod(methodName1, protoItem1, ACC_PUBLIC | ACC_STATIC, params1);
964 
965     StringItem *methodName2 = container.GetOrCreateStringItem("foo2");
966 
967     PrimitiveTypeItem *retType2 = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32);
968     std::vector<MethodParamItem> params2;
969     params2.emplace_back(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::F32));
970     ProtoItem *protoItem2 = container.GetOrCreateProtoItem(retType2, params2);
971 
972     classItem->AddMethod(methodName2, protoItem2, ACC_PUBLIC | ACC_STATIC, params2);
973 
974     // Add field
975 
976     StringItem *fieldName = container.GetOrCreateStringItem("field");
977     PrimitiveTypeItem *fieldType = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32);
978 
979     classItem->AddField(fieldName, fieldType, ACC_PUBLIC);
980 
981     // Add source file
982 
983     StringItem *sourceFile = container.GetOrCreateStringItem("source_file");
984 
985     classItem->SetSourceFile(sourceFile);
986 
987     // Read items from container
988 
989     ASSERT_TRUE(container.GetItems().size() == 15);
990 
991     std::map<std::string, panda_file::BaseClassItem *> *classMap = container.GetClassMap();
992     ASSERT_TRUE(classMap != nullptr && classMap->size() == 2);
993     auto it = classMap->find("Bar");
994     ASSERT_TRUE(it != classMap->end());
995 
996     std::unordered_map<std::string, StringItem *> *stringMap = container.GetStringMap();
997     ASSERT_TRUE(stringMap != nullptr && stringMap->size() == 4);
998     auto sit0 = stringMap->find("field");
999     auto sit1 = stringMap->find("source_file");
1000     auto sit2 = stringMap->find("foo1");
1001     auto sit3 = stringMap->find("foo2");
1002     ASSERT_TRUE(sit0 != stringMap->end() && sit1 != stringMap->end() && sit2 != stringMap->end() &&
1003                 sit3 != stringMap->end());
1004 
1005     std::unordered_map<Type::TypeId, PrimitiveTypeItem *> *primitiveTypeMap = container.GetPrimitiveTypeMap();
1006     ASSERT_TRUE(primitiveTypeMap != nullptr && primitiveTypeMap->size() == 3);
1007     auto pit0 = primitiveTypeMap->find(Type::TypeId::F32);
1008     auto pit1 = primitiveTypeMap->find(Type::TypeId::I32);
1009     auto pit2 = primitiveTypeMap->find(Type::TypeId::VOID);
1010     ASSERT_TRUE(pit0 != primitiveTypeMap->end() && pit1 != primitiveTypeMap->end() && pit2 != primitiveTypeMap->end());
1011 
1012     auto *rclassItem = static_cast<panda_file::ClassItem *>(it->second);
1013     std::string methodName;
1014     std::function<bool(BaseItem *)> testMethod = [&](BaseItem *method) {
1015         auto *methodItem = static_cast<panda_file::MethodItem *>(method);
1016         ASSERT(methodItem != nullptr && methodItem->GetItemType() == ItemTypes::METHOD_ITEM);
1017         methodName = methodItem->GetNameItem()->GetData();
1018         methodName.pop_back();  // remove '\0'
1019         ASSERT(methodName == "foo1" || methodName == "foo2");
1020         return true;
1021     };
1022 
1023     using std::placeholders::_1;
1024     panda_file::BaseItem::VisitorCallBack cbMethod = testMethod;
1025     rclassItem->VisitMethods(cbMethod);
1026 
1027     std::string fName;
1028     std::function<bool(BaseItem *)> testField = [&](BaseItem *field) {
1029         auto *fieldItem = static_cast<panda_file::FieldItem *>(field);
1030         ASSERT(fieldItem != nullptr && fieldItem->GetItemType() == ItemTypes::FIELD_ITEM);
1031         fName = fieldItem->GetNameItem()->GetData();
1032         fName.pop_back();  // remove '\0'
1033         ASSERT(fName == "field");
1034         return true;
1035     };
1036 
1037     panda_file::BaseItem::VisitorCallBack cbField = testField;
1038     rclassItem->VisitFields(cbField);
1039 }
1040 
TEST(ItemContainer,AnnotationDeduplication)1041 TEST(ItemContainer, AnnotationDeduplication)
1042 {
1043     ItemContainer container;
1044 
1045     auto annot = container.GetOrCreateClassItem("Annot");
1046     annot->SetAccessFlags(ACC_PUBLIC | ACC_ANNOTATION);
1047     auto annot1 = container.CreateItem<AnnotationItem>(annot, std::vector<AnnotationItem::Elem> {},
1048                                                        std::vector<AnnotationItem::Tag> {});
1049     auto annot2 = container.CreateItem<AnnotationItem>(annot, std::vector<AnnotationItem::Elem> {},
1050                                                        std::vector<AnnotationItem::Tag> {});
1051 
1052     auto str = container.GetOrCreateStringItem("Abc");
1053 
1054     auto glb = container.GetOrCreateGlobalClassItem();
1055     auto proto = container.GetOrCreateProtoItem(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID), {});
1056     auto meth = glb->AddMethod(container.GetOrCreateStringItem("foo"), proto, ACC_STATIC | ACC_PUBLIC,
1057                                std::vector<MethodParamItem> {});
1058     meth->AddAnnotation(annot1);
1059     meth->AddAnnotation(annot2);
1060 
1061     std::vector<uint8_t> ins = {(uint8_t)BytecodeInstruction::Opcode::LDA_STR_ID32, 0, 0, 0, 0,
1062                                 (uint8_t)BytecodeInstruction::Opcode::RETURN_VOID};
1063     meth->SetCode(container.CreateItem<CodeItem>(0, 0, ins));
1064 
1065     container.ComputeLayout();
1066     for (size_t i = 0; i < 4; i++) {
1067         // NOLINTBEGIN(readability-magic-numbers)
1068         meth->GetCode()->GetInstructions()->at(1 + i) =
1069             static_cast<uint8_t>((str->GetFileId().GetOffset() >> (i * 8)) & 0xffU);
1070         // NOLINTEND(readability-magic-numbers)
1071     }
1072     container.DeduplicateItems(false);
1073 
1074     ASSERT_TRUE(!annot1->NeedsEmit() || !annot2->NeedsEmit());
1075 
1076     container.ComputeLayout();
1077     for (size_t i = 0; i < 4; i++) {
1078         // NOLINTBEGIN(readability-magic-numbers)
1079         ASSERT_EQ(meth->GetCode()->GetInstructions()->at(1 + i),
1080                   static_cast<uint8_t>((str->GetFileId().GetOffset() >> (i * 8)) & 0xffU));
1081         // NOLINTEND(readability-magic-numbers)
1082     }
1083 }
1084 
TEST(ItemContainer,AnnotationDeduplicationReading)1085 TEST(ItemContainer, AnnotationDeduplicationReading)
1086 {
1087     ItemContainer container;
1088 
1089     auto annot = container.GetOrCreateClassItem("Annot");
1090     annot->SetAccessFlags(ACC_PUBLIC | ACC_ANNOTATION);
1091     auto annot1 = container.CreateItem<AnnotationItem>(annot, std::vector<AnnotationItem::Elem> {},
1092                                                        std::vector<AnnotationItem::Tag> {});
1093     auto annot2 = container.CreateItem<AnnotationItem>(annot, std::vector<AnnotationItem::Elem> {},
1094                                                        std::vector<AnnotationItem::Tag> {});
1095 
1096     auto glb = container.GetOrCreateGlobalClassItem();
1097 
1098     glb->AddAnnotation(annot1);
1099     glb->AddAnnotation(annot2);
1100 
1101     size_t size = container.ComputeLayout();
1102 
1103     container.DeduplicateAnnotations();
1104     ASSERT_FALSE(annot1->NeedsEmit() && annot2->NeedsEmit());
1105     ASSERT_EQ(glb->GetAnnotations()->at(0), glb->GetAnnotations()->at(1));
1106     container.DeduplicateAnnotations();
1107 
1108     std::vector<uint8_t> memBuf(size);
1109     MemoryBufferWriter writer {memBuf.data(), memBuf.size()};
1110     ASSERT_EQ(glb->GetAnnotations()->at(0), glb->GetAnnotations()->at(1));
1111     container.Write(&writer);
1112 
1113     auto emptyDeleter = +[](std::byte *, size_t) noexcept {};
1114     auto reader = FileReader(File::OpenFromMemory(
1115         os::mem::ConstBytePtr(reinterpret_cast<std::byte *>(memBuf.data()), memBuf.size(), emptyDeleter)));
1116     ASSERT_TRUE(reader.ReadContainer());
1117 
1118     auto annots = reader.GetContainerPtr()->GetOrCreateGlobalClassItem()->GetAnnotations();
1119     ASSERT_EQ(annots->size(), 2);
1120     ASSERT_EQ(annots->at(0), annots->at(1));
1121     auto annot3 = reader.GetContainerPtr()->CreateItem<AnnotationItem>(
1122         reader.GetContainerPtr()->GetOrCreateClassItem("Annot"), std::vector<AnnotationItem::Elem> {},
1123         std::vector<AnnotationItem::Tag> {});
1124     annots->insert(annots->begin(), annot3);
1125     reader.GetContainerPtr()->DeduplicateItems(true);
1126     reader.GetContainerPtr()->DeduplicateItems(true);
1127 }
1128 
1129 }  // namespace ark::panda_file::test
1130