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