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