1 /**
2  * Copyright (c) 2021-2022 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 "debug_info_extractor.h"
21 #include "field_data_accessor-inl.h"
22 #include "file.h"
23 #include "file_item_container.h"
24 #include "file_writer.h"
25 #include "helpers.h"
26 #include "libpandabase/utils/logger.h"
27 #include "method_data_accessor-inl.h"
28 #include "method_handle_data_accessor.h"
29 #include "modifiers.h"
30 #include "os/file.h"
31 #include "proto_data_accessor-inl.h"
32 #include "pgo.h"
33 #include "value.h"
34 
35 #include "zlib.h"
36 
37 #include <cstddef>
38 
39 #include <memory>
40 #include <vector>
41 
42 #include <gtest/gtest.h>
43 #include <gmock/gmock.h>
44 
45 namespace panda::panda_file::test {
46 constexpr uint8_t ISA_VERSION_FIRST_NUMBER = panda::panda_file::version[0];
47 constexpr uint8_t ISA_VERSION_SECOND_NUMBER = panda::panda_file::version[1];
48 constexpr uint8_t ISA_VERSION_THIRD_NUMBER = panda::panda_file::version[2]; // 2: the third number of ISA version
49 constexpr uint8_t ISA_VERSION_FOURTH_NUMBER = panda::panda_file::version[3]; // 3: the fourth number of ISA version
50 constexpr size_t ELEM_WIDTH = 4;
51 constexpr size_t ELEM_PER16 = 16 / ELEM_WIDTH;
52 constexpr uint8_t API_VERSION = 11;
53 
HWTEST(ItemContainer, DeduplicationTest, testing::ext::TestSize.Level0)54 HWTEST(ItemContainer, DeduplicationTest, testing::ext::TestSize.Level0)
55 {
56     ItemContainer container;
57 
58     StringItem *string_item = container.GetOrCreateStringItem("1");
59     EXPECT_EQ(string_item, container.GetOrCreateStringItem("1"));
60 
61     ClassItem *class_item = container.GetOrCreateClassItem("1");
62     EXPECT_EQ(class_item, container.GetOrCreateClassItem("1"));
63 
64     ValueItem *int_item = container.GetOrCreateIntegerValueItem(1);
65     EXPECT_EQ(int_item, container.GetOrCreateIntegerValueItem(1));
66 
67     ValueItem *long_item = container.GetOrCreateLongValueItem(1);
68     EXPECT_EQ(long_item, container.GetOrCreateLongValueItem(1));
69     EXPECT_NE(long_item, int_item);
70 
71     ValueItem *float_item = container.GetOrCreateFloatValueItem(1.0);
72     EXPECT_EQ(float_item, container.GetOrCreateFloatValueItem(1.0));
73     EXPECT_NE(float_item, int_item);
74     EXPECT_NE(float_item, long_item);
75 
76     ValueItem *double_item = container.GetOrCreateDoubleValueItem(1.0);
77     EXPECT_EQ(double_item, container.GetOrCreateDoubleValueItem(1.0));
78     EXPECT_NE(double_item, int_item);
79     EXPECT_NE(double_item, long_item);
80     EXPECT_NE(double_item, float_item);
81 }
82 
HWTEST(ItemContainer, TestFileOpen, testing::ext::TestSize.Level0)83 HWTEST(ItemContainer, TestFileOpen, testing::ext::TestSize.Level0)
84 {
85     using panda::os::file::Mode;
86     using panda::os::file::Open;
87 
88     // Write panda file to disk
89     ItemContainer container;
90 
91     const std::string file_name = "test_file_open.panda";
92     auto writer = FileWriter(file_name);
93 
94     ASSERT_TRUE(container.Write(&writer));
95 
96     // Read panda file from disk
97     EXPECT_NE(File::Open(file_name), nullptr);
98 }
99 
HWTEST(ItemContainer, TestFileFormatVersionTooOldDeathTest, testing::ext::TestSize.Level0)100 HWTEST(ItemContainer, TestFileFormatVersionTooOldDeathTest, testing::ext::TestSize.Level0)
101 {
102     const std::string file_name = "test_file_format_version_too_old.abc";
103     {
104         ItemContainer container;
105         auto writer = FileWriter(file_name);
106 
107         File::Header header;
108         errno_t res = memset_s(&header, sizeof(header), 0, sizeof(header));
109         if (res != EOK) {
110             UNREACHABLE();
111         }
112         header.magic = File::MAGIC;
113 
114         auto old = std::array<uint8_t, File::VERSION_SIZE>(minVersion);
115         --old[3];
116 
117         header.version = old;
118         header.file_size = sizeof(File::Header);
119 
120         for (uint8_t b : Span<uint8_t>(reinterpret_cast<uint8_t *>(&header), sizeof(header))) {
121             writer.WriteByte(b);
122         }
123         EXPECT_TRUE(writer.FinishWrite());
124     }
125 
126 #ifdef HOST_UT
127     EXPECT_DEATH(File::Open(file_name), ".*");
128 #endif
129 }
130 
HWTEST(ItemContainer, TestRewriteChecksum, testing::ext::TestSize.Level0)131 HWTEST(ItemContainer, TestRewriteChecksum, testing::ext::TestSize.Level0)
132 {
133     const std::string file_name = "test_rewrite_checksum.abc";
134     {
135         ItemContainer container;
136         auto writer = FileWriter(file_name);
137         File::Header header;
138         errno_t res = memset_s(&header, sizeof(header), 0, sizeof(header));
139         EXPECT_EQ(res, EOK);
140         header.magic = File::MAGIC;
141         header.version = std::array<uint8_t, File::VERSION_SIZE>(minVersion);
142         header.file_size = sizeof(File::Header);
143         EXPECT_EQ(header.checksum, 0u);
144         writer.CountChecksum(true);
145         for (uint8_t b : Span<uint8_t>(reinterpret_cast<uint8_t *>(&header), sizeof(header))) {
146             writer.WriteByte(b);
147         }
148         EXPECT_NE(writer.GetChecksum(), 0u);
149         writer.CountChecksum(false);
150         size_t offset = static_cast<size_t>(reinterpret_cast<uint8_t *>(&(header.checksum)) -
151             reinterpret_cast<uint8_t *>(&header));
152         EXPECT_TRUE(writer.RewriteChecksum(offset));
153         EXPECT_TRUE(writer.FinishWrite());
154     }
155     const auto &file = File::Open(file_name);
156     EXPECT_NE(file, nullptr);
157     EXPECT_NE(file->GetHeader()->checksum, 0u);
158 }
159 
HWTEST(ItemContainer, TestReserveBufferCapacity, testing::ext::TestSize.Level0)160 HWTEST(ItemContainer, TestReserveBufferCapacity, testing::ext::TestSize.Level0)
161 {
162     const std::string file_name = "test_reserve_buffer_capacity.abc";
163     {
164         auto writer = FileWriter(file_name);
165         static constexpr size_t CAPACITY = 2000u;
166         EXPECT_LT(writer.GetBuffer().capacity(), CAPACITY);
167         writer.ReserveBufferCapacity(CAPACITY);
168         EXPECT_GE(writer.GetBuffer().capacity(), CAPACITY);
169     }
170 }
171 
HWTEST(ItemContainer, TestFileFormatVersionTooNewDeathTest, testing::ext::TestSize.Level0)172 HWTEST(ItemContainer, TestFileFormatVersionTooNewDeathTest, testing::ext::TestSize.Level0)
173 {
174     const std::string file_name = "test_file_format_version_too_new.abc";
175     {
176         ItemContainer container;
177         auto writer = FileWriter(file_name);
178 
179         File::Header header;
180         errno_t res = memset_s(&header, sizeof(header), 0, sizeof(header));
181         if (res != EOK) {
182             UNREACHABLE();
183         }
184         header.magic = File::MAGIC;
185         if (ISA_VERSION_FIRST_NUMBER + 1 < 256 && ISA_VERSION_SECOND_NUMBER + 1 < 256) {
186             header.version = {ISA_VERSION_FIRST_NUMBER + 1, ISA_VERSION_SECOND_NUMBER + 1,
187                 ISA_VERSION_THIRD_NUMBER, ISA_VERSION_FOURTH_NUMBER};
188         } else {
189             header.version = {ISA_VERSION_FIRST_NUMBER, ISA_VERSION_SECOND_NUMBER,
190                 ISA_VERSION_THIRD_NUMBER, ISA_VERSION_FOURTH_NUMBER};
191         }
192         header.file_size = sizeof(File::Header);
193 
194         for (uint8_t b : Span<uint8_t>(reinterpret_cast<uint8_t *>(&header), sizeof(header))) {
195             writer.WriteByte(b);
196         }
197         EXPECT_TRUE(writer.FinishWrite());
198     }
199 
200 #ifdef HOST_UT
201     EXPECT_DEATH(File::Open(file_name), ".*");
202 #endif
203 }
204 
HWTEST(ItemContainer, TestFileFormatVersionValid, testing::ext::TestSize.Level0)205 HWTEST(ItemContainer, TestFileFormatVersionValid, testing::ext::TestSize.Level0)
206 {
207     const std::string file_name = "test_file_format_version_valid.abc";
208     {
209         ItemContainer container;
210         auto writer = FileWriter(file_name);
211 
212         File::Header header;
213         errno_t res = memset_s(&header, sizeof(header), 0, sizeof(header));
214         if (res != EOK) {
215             UNREACHABLE();
216         }
217         header.magic = File::MAGIC;
218         header.version = {0, 0, 0, 2};
219         header.file_size = sizeof(File::Header);
220 
221         for (uint8_t b : Span<uint8_t>(reinterpret_cast<uint8_t *>(&header), sizeof(header))) {
222             writer.WriteByte(b);
223         }
224         EXPECT_TRUE(writer.FinishWrite());
225     }
226 
227     EXPECT_NE(File::Open(file_name), nullptr);
228 }
229 
GetPandaFile(std::vector<uint8_t> &data)230 static std::unique_ptr<const File> GetPandaFile(std::vector<uint8_t> &data)
231 {
232     os::mem::ConstBytePtr ptr(reinterpret_cast<std::byte *>(data.data()), data.size(),
233                               [](std::byte *, size_t) noexcept {});
234     return File::OpenFromMemory(std::move(ptr));
235 }
236 
TestPandaFile(MemoryWriter& mem_writer, std::unique_ptr<const File>& panda_file, ClassItem* class_item, ClassItem* empty_class_item, StringItem* source_file, ClassDataAccessor class_data_accessor)237 void TestPandaFile(MemoryWriter& mem_writer, std::unique_ptr<const File>& panda_file,
238                    ClassItem* class_item, ClassItem* empty_class_item, StringItem* source_file,
239                    ClassDataAccessor class_data_accessor)
240 {
241     EXPECT_THAT(panda_file->GetHeader()->version, ::testing::ElementsAre(ISA_VERSION_FIRST_NUMBER,
242         ISA_VERSION_SECOND_NUMBER, ISA_VERSION_THIRD_NUMBER, ISA_VERSION_FOURTH_NUMBER));
243     EXPECT_EQ(panda_file->GetHeader()->file_size, mem_writer.GetData().size());
244     EXPECT_EQ(panda_file->GetHeader()->foreign_off, 0U);
245     EXPECT_EQ(panda_file->GetHeader()->foreign_size, 0U);
246     EXPECT_EQ(panda_file->GetHeader()->num_classes, 3U);
247     EXPECT_EQ(panda_file->GetHeader()->class_idx_off, sizeof(File::Header));
248 
249     const uint32_t *class_index =
250         reinterpret_cast<const uint32_t *>(panda_file->GetBase() + panda_file->GetHeader()->class_idx_off);
251     EXPECT_EQ(class_index[0], class_item->GetOffset());
252     EXPECT_EQ(class_index[1], empty_class_item->GetOffset());
253 
254     EXPECT_EQ(class_data_accessor.GetSuperClassId().GetOffset(), empty_class_item->GetOffset());
255     EXPECT_EQ(class_data_accessor.GetAccessFlags(), ACC_PUBLIC);
256     EXPECT_EQ(class_data_accessor.GetFieldsNumber(), 1U);
257     EXPECT_EQ(class_data_accessor.GetMethodsNumber(), 1U);
258     EXPECT_EQ(class_data_accessor.GetIfacesNumber(), 1U);
259     EXPECT_TRUE(class_data_accessor.GetSourceFileId().has_value());
260     EXPECT_EQ(class_data_accessor.GetSourceFileId().value().GetOffset(), source_file->GetOffset());
261     EXPECT_EQ(class_data_accessor.GetSize(), class_item->GetSize());
262 }
263 
TestClassDataAccessor(std::unique_ptr<const File>& panda_file, ClassItem* class_item, ClassItem* iface_item, AnnotationItem* runtime_annotation_item, ClassDataAccessor class_data_accessor)264 void TestClassDataAccessor(std::unique_ptr<const File>& panda_file, ClassItem* class_item, ClassItem* iface_item,
265                            AnnotationItem* runtime_annotation_item, ClassDataAccessor class_data_accessor)
266 {
267     class_data_accessor.EnumerateInterfaces([&](File::EntityId id) {
268         EXPECT_EQ(id.GetOffset(), iface_item->GetOffset());
269 
270         ClassDataAccessor iface_class_data_accessor(*panda_file, id);
271         EXPECT_EQ(iface_class_data_accessor.GetSuperClassId().GetOffset(), 0U);
272         EXPECT_EQ(iface_class_data_accessor.GetAccessFlags(), ACC_PUBLIC);
273         EXPECT_EQ(iface_class_data_accessor.GetFieldsNumber(), 0U);
274         EXPECT_EQ(iface_class_data_accessor.GetMethodsNumber(), 0U);
275         EXPECT_EQ(iface_class_data_accessor.GetIfacesNumber(), 0U);
276         EXPECT_FALSE(iface_class_data_accessor.GetSourceFileId().has_value());
277         EXPECT_EQ(iface_class_data_accessor.GetSize(), iface_item->GetSize());
278     });
279 
280     class_data_accessor.EnumerateRuntimeAnnotations([&](File::EntityId id) {
281         EXPECT_EQ(id.GetOffset(), runtime_annotation_item->GetOffset());
282 
283         AnnotationDataAccessor data_accessor(*panda_file, id);
284         EXPECT_EQ(data_accessor.GetAnnotationId().GetOffset(), runtime_annotation_item->GetOffset());
285         EXPECT_EQ(data_accessor.GetClassId().GetOffset(), class_item->GetOffset());
286         EXPECT_EQ(data_accessor.GetCount(), 0U);
287     });
288 }
289 
TestAnnotationsAndFields(ClassDataAccessor class_data_accessor, AnnotationItem* annotation_item, std::unique_ptr<const File>& panda_file, ClassItem* class_item, FieldItem* field_item, StringItem* field_name)290 void TestAnnotationsAndFields(ClassDataAccessor class_data_accessor, AnnotationItem* annotation_item,
291                               std::unique_ptr<const File>& panda_file, ClassItem* class_item,
292                               FieldItem* field_item, StringItem* field_name)
293 {
294     class_data_accessor.EnumerateAnnotations([&](File::EntityId id) {
295         EXPECT_EQ(id.GetOffset(), annotation_item->GetOffset());
296 
297         AnnotationDataAccessor data_accessor(*panda_file, id);
298         EXPECT_EQ(data_accessor.GetAnnotationId().GetOffset(), annotation_item->GetOffset());
299         EXPECT_EQ(data_accessor.GetClassId().GetOffset(), class_item->GetOffset());
300         EXPECT_EQ(data_accessor.GetCount(), 0U);
301     });
302 
303     class_data_accessor.EnumerateFields([&](FieldDataAccessor &data_accessor) {
304         EXPECT_EQ(data_accessor.GetFieldId().GetOffset(), field_item->GetOffset());
305         EXPECT_EQ(data_accessor.GetClassId().GetOffset(), class_item->GetOffset());
306         EXPECT_EQ(data_accessor.GetNameId().GetOffset(), field_name->GetOffset());
307         EXPECT_EQ(data_accessor.GetType(), Type(Type::TypeId::I32).GetFieldEncoding());
308         EXPECT_EQ(data_accessor.GetAccessFlags(), ACC_PUBLIC);
309         EXPECT_FALSE(data_accessor.GetValue<int32_t>().has_value());
310         EXPECT_EQ(data_accessor.GetSize(), field_item->GetSize());
311 
312         data_accessor.EnumerateRuntimeAnnotations([](File::EntityId) { EXPECT_TRUE(false); });
313         data_accessor.EnumerateAnnotations([](File::EntityId) { EXPECT_TRUE(false); });
314     });
315 }
316 
TestMethods(ClassDataAccessor class_data_accessor, MethodItem* method_item, ClassItem* class_item, StringItem* method_name, ProtoItem* proto_item)317 void TestMethods(ClassDataAccessor class_data_accessor, MethodItem* method_item, ClassItem* class_item,
318                  StringItem* method_name, ProtoItem* proto_item)
319 {
320     class_data_accessor.EnumerateMethods([&](MethodDataAccessor &data_accessor) {
321         EXPECT_FALSE(data_accessor.IsExternal());
322         EXPECT_EQ(data_accessor.GetMethodId().GetOffset(), method_item->GetOffset());
323         EXPECT_EQ(data_accessor.GetClassId().GetOffset(), class_item->GetOffset());
324         EXPECT_EQ(data_accessor.GetNameId().GetOffset(), method_name->GetOffset());
325         EXPECT_EQ(data_accessor.GetProtoId().GetOffset(), proto_item->GetOffset());
326         EXPECT_EQ(data_accessor.GetAccessFlags(), ACC_PUBLIC | ACC_STATIC);
327         EXPECT_FALSE(data_accessor.GetCodeId().has_value());
328         EXPECT_EQ(data_accessor.GetSize(), method_item->GetSize());
329         EXPECT_FALSE(data_accessor.GetRuntimeParamAnnotationId().has_value());
330         EXPECT_FALSE(data_accessor.GetParamAnnotationId().has_value());
331         EXPECT_FALSE(data_accessor.GetDebugInfoId().has_value());
332 
333         data_accessor.EnumerateRuntimeAnnotations([](File::EntityId) { EXPECT_TRUE(false); });
334         data_accessor.EnumerateAnnotations([](File::EntityId) { EXPECT_TRUE(false); });
335     });
336 }
337 
TestEmptyClassDataAccessor(std::unique_ptr<const File> &panda_file, ClassItem *empty_class_item)338 void TestEmptyClassDataAccessor(std::unique_ptr<const File> &panda_file, ClassItem *empty_class_item)
339 {
340     ClassDataAccessor empty_class_data_accessor(*panda_file, File::EntityId(empty_class_item->GetOffset()));
341     EXPECT_EQ(empty_class_data_accessor.GetSuperClassId().GetOffset(), 0U);
342     EXPECT_EQ(empty_class_data_accessor.GetAccessFlags(), 0U);
343     EXPECT_EQ(empty_class_data_accessor.GetFieldsNumber(), 0U);
344     EXPECT_EQ(empty_class_data_accessor.GetMethodsNumber(), 0U);
345     EXPECT_EQ(empty_class_data_accessor.GetIfacesNumber(), 0U);
346     EXPECT_FALSE(empty_class_data_accessor.GetSourceFileId().has_value());
347     EXPECT_EQ(empty_class_data_accessor.GetSize(), empty_class_item->GetSize());
348 }
349 
HWTEST(ItemContainer, TestClasses, testing::ext::TestSize.Level0)350 HWTEST(ItemContainer, TestClasses, testing::ext::TestSize.Level0)
351 {
352     // Write panda file to memory
353 
354     ItemContainer container;
355 
356     ClassItem *empty_class_item = container.GetOrCreateClassItem("Foo");
357 
358     ClassItem *class_item = container.GetOrCreateClassItem("Bar");
359     class_item->SetAccessFlags(ACC_PUBLIC);
360     class_item->SetSuperClass(empty_class_item);
361 
362     // Add interface
363 
364     ClassItem *iface_item = container.GetOrCreateClassItem("Iface");
365     iface_item->SetAccessFlags(ACC_PUBLIC);
366 
367     class_item->AddInterface(iface_item);
368 
369     // Add method
370 
371     StringItem *method_name = container.GetOrCreateStringItem("foo");
372 
373     PrimitiveTypeItem *ret_type = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
374     std::vector<MethodParamItem> params;
375     ProtoItem *proto_item = container.GetOrCreateProtoItem(ret_type, params);
376 
377     MethodItem *method_item = class_item->AddMethod(method_name, proto_item, ACC_PUBLIC | ACC_STATIC, params);
378 
379     // Add field
380 
381     StringItem *field_name = container.GetOrCreateStringItem("field");
382     PrimitiveTypeItem *field_type = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32);
383 
384     FieldItem *field_item = class_item->AddField(field_name, field_type, ACC_PUBLIC);
385 
386     // Add runtime annotation
387 
388     std::vector<AnnotationItem::Elem> runtime_elems;
389     std::vector<AnnotationItem::Tag> runtime_tags;
390     AnnotationItem *runtime_annotation_item =
391         container.CreateItem<AnnotationItem>(class_item, runtime_elems, runtime_tags);
392 
393     class_item->AddRuntimeAnnotation(runtime_annotation_item);
394 
395     // Add annotation
396 
397     std::vector<AnnotationItem::Elem> elems;
398     std::vector<AnnotationItem::Tag> tags;
399     AnnotationItem *annotation_item = container.CreateItem<AnnotationItem>(class_item, elems, tags);
400 
401     class_item->AddAnnotation(annotation_item);
402 
403     // Add source file
404 
405     StringItem *source_file = container.GetOrCreateStringItem("source_file");
406 
407     class_item->SetSourceFile(source_file);
408 
409     MemoryWriter mem_writer;
410 
411     ASSERT_TRUE(container.Write(&mem_writer));
412 
413     // Read panda file from memory
414 
415     auto data = mem_writer.GetData();
416     auto panda_file = GetPandaFile(data);
417 
418     ASSERT_NE(panda_file, nullptr);
419 
420     std::vector<uint8_t> class_name {'B', 'a', 'r', '\0'};
421     auto class_id = panda_file->GetClassId(class_name.data());
422     EXPECT_EQ(class_id.GetOffset(), class_item->GetOffset());
423 
424     ClassDataAccessor class_data_accessor(*panda_file, class_id);
425 
426     TestPandaFile(mem_writer, panda_file, class_item, empty_class_item, source_file, class_data_accessor);
427     TestClassDataAccessor(panda_file, class_item, iface_item, runtime_annotation_item, class_data_accessor);
428 
429     // Annotation is the same as the runtime one, so we deduplicate it
430     EXPECT_FALSE(annotation_item->NeedsEmit());
431     annotation_item = runtime_annotation_item;
432 
433     TestAnnotationsAndFields(class_data_accessor, annotation_item, panda_file, class_item, field_item, field_name);
434 
435     TestMethods(class_data_accessor, method_item, class_item, method_name, proto_item);
436 
437     TestEmptyClassDataAccessor(panda_file, empty_class_item);
438 }
439 
HWTEST(ItemContainer, TestMethods, testing::ext::TestSize.Level0)440 HWTEST(ItemContainer, TestMethods, testing::ext::TestSize.Level0)
441 {
442     // Write panda file to memory
443 
444     ItemContainer container;
445 
446     ClassItem *class_item = container.GetOrCreateClassItem("A");
447     class_item->SetAccessFlags(ACC_PUBLIC);
448 
449     StringItem *method_name = container.GetOrCreateStringItem("foo");
450 
451     PrimitiveTypeItem *ret_type = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
452     std::vector<MethodParamItem> params;
453     ProtoItem *proto_item = container.GetOrCreateProtoItem(ret_type, params);
454 
455     MethodItem *method_item = class_item->AddMethod(method_name, proto_item, ACC_PUBLIC | ACC_STATIC, params);
456 
457     std::vector<uint8_t> instructions {1, 2, 3, 4};
458     CodeItem *code_item = container.CreateItem<CodeItem>(0, 2, instructions);
459 
460     method_item->SetCode(code_item);
461 
462     MemoryWriter mem_writer;
463 
464     ASSERT_TRUE(container.Write(&mem_writer));
465 
466     // Read panda file from memory
467 
468     auto data = mem_writer.GetData();
469     auto panda_file = GetPandaFile(data);
470 
471     ASSERT_NE(panda_file, nullptr);
472 
473     ClassDataAccessor class_data_accessor(*panda_file, File::EntityId(class_item->GetOffset()));
474 
475     class_data_accessor.EnumerateMethods([&](MethodDataAccessor &data_accessor) {
476         EXPECT_FALSE(data_accessor.IsExternal());
477         EXPECT_EQ(data_accessor.GetMethodId().GetOffset(), method_item->GetOffset());
478         EXPECT_EQ(data_accessor.GetClassId().GetOffset(), class_item->GetOffset());
479         EXPECT_EQ(data_accessor.GetNameId().GetOffset(), method_name->GetOffset());
480         EXPECT_EQ(data_accessor.GetProtoId().GetOffset(), proto_item->GetOffset());
481         EXPECT_EQ(data_accessor.GetAccessFlags(), ACC_PUBLIC | ACC_STATIC);
482         EXPECT_EQ(data_accessor.GetSize(), method_item->GetSize());
483 
484         auto code_id = data_accessor.GetCodeId();
485         EXPECT_TRUE(code_id.has_value());
486         EXPECT_EQ(code_id.value().GetOffset(), code_item->GetOffset());
487 
488         CodeDataAccessor code_data_accessor(*panda_file, code_id.value());
489         EXPECT_EQ(code_data_accessor.GetNumVregs(), 0U);
490         EXPECT_EQ(code_data_accessor.GetNumArgs(), 2U);
491         EXPECT_EQ(code_data_accessor.GetCodeSize(), instructions.size());
492         EXPECT_THAT(instructions, ::testing::ElementsAreArray(code_data_accessor.GetInstructions(),
493                                                               code_data_accessor.GetCodeSize()));
494 
495         EXPECT_EQ(code_data_accessor.GetTriesSize(), 0U);
496         EXPECT_EQ(code_data_accessor.GetSize(), code_item->GetSize());
497 
498         code_data_accessor.EnumerateTryBlocks([](const CodeDataAccessor::TryBlock &) {
499             EXPECT_TRUE(false);
500             return false;
501         });
502 
503         EXPECT_FALSE(data_accessor.GetDebugInfoId().has_value());
504 
505         EXPECT_FALSE(data_accessor.GetRuntimeParamAnnotationId().has_value());
506 
507         EXPECT_FALSE(data_accessor.GetParamAnnotationId().has_value());
508 
509         data_accessor.EnumerateRuntimeAnnotations([](File::EntityId) { EXPECT_TRUE(false); });
510 
511         data_accessor.EnumerateAnnotations([](File::EntityId) { EXPECT_TRUE(false); });
512     });
513 }
514 
TestProtoDataAccessor(MemoryWriter& mem_writer, ClassItem* class_item, MethodItem* method_item, ProtoItem* proto_item, std::vector<Type::TypeId> types, std::vector<ClassItem *> ref_types)515 void TestProtoDataAccessor(MemoryWriter& mem_writer, ClassItem* class_item, MethodItem* method_item,
516                            ProtoItem* proto_item, std::vector<Type::TypeId> types, std::vector<ClassItem *> ref_types)
517 {
518     auto data = mem_writer.GetData();
519     auto panda_file = GetPandaFile(data);
520 
521     ASSERT_NE(panda_file, nullptr);
522 
523     ClassDataAccessor class_data_accessor(*panda_file, File::EntityId(class_item->GetOffset()));
524 
525     class_data_accessor.EnumerateMethods([&](MethodDataAccessor &data_accessor) {
526         EXPECT_EQ(data_accessor.GetMethodId().GetOffset(), method_item->GetOffset());
527         EXPECT_EQ(data_accessor.GetProtoId().GetOffset(), proto_item->GetOffset());
528 
529         ProtoDataAccessor proto_data_accessor(*panda_file, data_accessor.GetProtoId());
530         EXPECT_EQ(proto_data_accessor.GetProtoId().GetOffset(), proto_item->GetOffset());
531 
532         size_t num = 0;
533         size_t nref = 0;
534         proto_data_accessor.EnumerateTypes([&](Type t) {
535             EXPECT_EQ(t.GetEncoding(), Type(types[num]).GetEncoding());
536             ++num;
537 
538             if (!t.IsPrimitive()) {
539                 ++nref;
540             }
541         });
542 
543         EXPECT_EQ(num, types.size());
544 
545         for (size_t i = 0; i < num - 1; i++) {
546             EXPECT_EQ(proto_data_accessor.GetArgType(i).GetEncoding(), Type(types[i + 1]).GetEncoding());
547         }
548 
549         EXPECT_EQ(proto_data_accessor.GetReturnType().GetEncoding(), Type(types[0]).GetEncoding());
550 
551         EXPECT_EQ(nref, ref_types.size());
552 
553         for (size_t i = 0; i < nref; i++) {
554             EXPECT_EQ(proto_data_accessor.GetReferenceType(0).GetOffset(), ref_types[i]->GetOffset());
555         }
556 
557         size_t size = ((num + ELEM_PER16) / ELEM_PER16 + nref) * sizeof(uint16_t);
558 
559         EXPECT_EQ(proto_data_accessor.GetSize(), size);
560         EXPECT_EQ(proto_data_accessor.GetSize(), proto_item->GetSize());
561     });
562 }
563 
TestProtos(size_t n)564 void TestProtos(size_t n)
565 {
566     // Write panda file to memory
567     ItemContainer::SetApi(API_VERSION);
568     ItemContainer container;
569 
570     ClassItem *class_item = container.GetOrCreateClassItem("A");
571     class_item->SetAccessFlags(ACC_PUBLIC);
572 
573     StringItem *method_name = container.GetOrCreateStringItem("foo");
574 
575     std::vector<Type::TypeId> types {Type::TypeId::VOID, Type::TypeId::I32};
576     std::vector<ClassItem *> ref_types;
577 
578     PrimitiveTypeItem *ret_type = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
579     std::vector<MethodParamItem> params;
580 
581     params.emplace_back(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32));
582 
583     for (size_t i = 0; i < ELEM_PER16 * 2 - 2; i++) {
584         params.emplace_back(container.GetOrCreateClassItem("B"));
585         types.push_back(Type::TypeId::REFERENCE);
586         ref_types.push_back(container.GetOrCreateClassItem("B"));
587         params.emplace_back(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::F64));
588         types.push_back(Type::TypeId::F64);
589     }
590 
591     for (size_t i = 0; i < n; i++) {
592         params.emplace_back(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::F32));
593         types.push_back(Type::TypeId::F32);
594     }
595 
596     ProtoItem *proto_item = container.GetOrCreateProtoItem(ret_type, params);
597 
598     MethodItem *method_item = class_item->AddMethod(method_name, proto_item, ACC_PUBLIC | ACC_STATIC, params);
599 
600     MemoryWriter mem_writer;
601 
602     ASSERT_TRUE(container.Write(&mem_writer));
603 
604     // Read panda file from memory
605 
606     TestProtoDataAccessor(mem_writer, class_item, method_item, proto_item, types, ref_types);
607 }
608 
HWTEST(ItemContainer, TestProtos, testing::ext::TestSize.Level0)609 HWTEST(ItemContainer, TestProtos, testing::ext::TestSize.Level0)
610 {
611     TestProtos(0);
612     TestProtos(1);
613     TestProtos(2);
614     TestProtos(7);
615 }
616 
HWTEST(ItemContainer, TestDebugInfo, testing::ext::TestSize.Level0)617 HWTEST(ItemContainer, TestDebugInfo, testing::ext::TestSize.Level0)
618 {
619     // Write panda file to memory
620 
621     ItemContainer container;
622 
623     ClassItem *class_item = container.GetOrCreateClassItem("A");
624     class_item->SetAccessFlags(ACC_PUBLIC);
625 
626     StringItem *method_name = container.GetOrCreateStringItem("foo");
627 
628     PrimitiveTypeItem *ret_type = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
629     std::vector<MethodParamItem> params;
630     params.emplace_back(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32));
631     ProtoItem *proto_item = container.GetOrCreateProtoItem(ret_type, params);
632     MethodItem *method_item = class_item->AddMethod(method_name, proto_item, ACC_PUBLIC | ACC_STATIC, params);
633 
634     StringItem *source_file_item = container.GetOrCreateStringItem("<source>");
635     StringItem *source_code_item = container.GetOrCreateStringItem("let a = 1;");
636     StringItem *param_string_item = container.GetOrCreateStringItem("a0");
637 
638     LineNumberProgramItem *line_number_program_item = container.CreateLineNumberProgramItem();
639     DebugInfoItem *debug_info_item = container.CreateItem<DebugInfoItem>(line_number_program_item);
640     method_item->SetDebugInfo(debug_info_item);
641 
642     // Create foreign class
643     ForeignClassItem *class_item_foreign = container.GetOrCreateForeignClassItem("ForeignClass");
644 
645     // Create foreign method
646     StringItem *method_name_foreign = container.GetOrCreateStringItem("ForeignMethod");
647     PrimitiveTypeItem *ret_type_foreign = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
648     std::vector<MethodParamItem> params_foreign;
649     params_foreign.emplace_back(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32));
650     ProtoItem *proto_item_foreign = container.GetOrCreateProtoItem(ret_type_foreign, params_foreign);
651     container.CreateItem<ForeignMethodItem>(class_item_foreign,
652         method_name_foreign, proto_item_foreign, 0);
653 
654     // Add debug info
655 
656     container.ComputeLayout();
657 
658     std::vector<uint8_t> opcodes {
659         static_cast<uint8_t>(LineNumberProgramItem::Opcode::SET_SOURCE_CODE),
660         static_cast<uint8_t>(LineNumberProgramItem::Opcode::SET_FILE),
661         static_cast<uint8_t>(LineNumberProgramItem::Opcode::SET_PROLOGUE_END),
662         static_cast<uint8_t>(LineNumberProgramItem::Opcode::ADVANCE_PC),
663         static_cast<uint8_t>(LineNumberProgramItem::Opcode::ADVANCE_LINE),
664         static_cast<uint8_t>(LineNumberProgramItem::Opcode::SET_EPILOGUE_BEGIN),
665         static_cast<uint8_t>(LineNumberProgramItem::Opcode::END_SEQUENCE),
666     };
667 
668     auto *constant_pool = debug_info_item->GetConstantPool();
669     debug_info_item->SetLineNumber(5);
670     line_number_program_item->EmitSetSourceCode(constant_pool, source_code_item);
671     line_number_program_item->EmitSetFile(constant_pool, source_file_item);
672     line_number_program_item->EmitPrologEnd();
673     line_number_program_item->EmitAdvancePc(constant_pool, 10);
674     line_number_program_item->EmitAdvanceLine(constant_pool, -5);
675     line_number_program_item->EmitEpilogBegin();
676     line_number_program_item->EmitEnd();
677 
678     debug_info_item->AddParameter(param_string_item);
679 
680     method_item->SetDebugInfo(debug_info_item);
681 
682     MemoryWriter mem_writer;
683 
684     ASSERT_TRUE(container.Write(&mem_writer));
685 
686     // Read panda file from memory
687 
688     auto data = mem_writer.GetData();
689     auto panda_file = GetPandaFile(data);
690 
691     ASSERT_NE(panda_file, nullptr);
692 
693     ClassDataAccessor class_data_accessor(*panda_file, File::EntityId(class_item->GetOffset()));
694 
695     class_data_accessor.EnumerateMethods([&](MethodDataAccessor &data_accessor) {
696         EXPECT_EQ(data_accessor.GetMethodId().GetOffset(), method_item->GetOffset());
697         EXPECT_EQ(data_accessor.GetSize(), method_item->GetSize());
698 
699         auto debug_info_id = data_accessor.GetDebugInfoId();
700         EXPECT_TRUE(debug_info_id.has_value());
701 
702         EXPECT_EQ(debug_info_id.value().GetOffset(), debug_info_item->GetOffset());
703 
704         DebugInfoDataAccessor dda(*panda_file, debug_info_id.value());
705         EXPECT_EQ(dda.GetDebugInfoId().GetOffset(), debug_info_item->GetOffset());
706         EXPECT_EQ(dda.GetLineStart(), 5U);
707         EXPECT_EQ(dda.GetNumParams(), params.size());
708 
709         dda.EnumerateParameters([&](File::EntityId id) { EXPECT_EQ(id.GetOffset(), param_string_item->GetOffset()); });
710 
711         auto cp = dda.GetConstantPool();
712         EXPECT_EQ(cp.size(), constant_pool->size());
713         EXPECT_THAT(*constant_pool, ::testing::ElementsAreArray(cp.data(), cp.Size()));
714 
715         EXPECT_EQ(helpers::ReadULeb128(&cp), source_code_item->GetOffset());
716         EXPECT_EQ(helpers::ReadULeb128(&cp), source_file_item->GetOffset());
717         EXPECT_EQ(helpers::ReadULeb128(&cp), 10U);
718         EXPECT_EQ(helpers::ReadLeb128(&cp), -5);
719 
720         const uint8_t *line_number_program = dda.GetLineNumberProgram();
721         EXPECT_EQ(panda_file->GetIdFromPointer(line_number_program).GetOffset(), line_number_program_item->GetOffset());
722         EXPECT_EQ(line_number_program_item->GetSize(), opcodes.size());
723 
724         EXPECT_THAT(opcodes, ::testing::ElementsAreArray(line_number_program, opcodes.size()));
725 
726         EXPECT_EQ(dda.GetSize(), debug_info_item->GetSize());
727     });
728 
729     DebugInfoExtractor extractor(panda_file.get());
730     const auto &methods = extractor.GetMethodIdList();
731     for (const auto &method_id : methods) {
732         for (const auto &info : extractor.GetParameterInfo(method_id)) {
733             EXPECT_EQ(info.name, "a0");
734         }
735     }
736 }
737 
HWTEST(ItemContainer, ForeignItems, testing::ext::TestSize.Level0)738 HWTEST(ItemContainer, ForeignItems, testing::ext::TestSize.Level0)
739 {
740     ItemContainer container;
741 
742     // Create foreign class
743     ForeignClassItem *class_item = container.GetOrCreateForeignClassItem("ForeignClass");
744 
745     // Create foreign field
746     StringItem *field_name = container.GetOrCreateStringItem("foreign_field");
747     PrimitiveTypeItem *field_type = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32);
748     ForeignFieldItem *field_item = container.CreateItem<ForeignFieldItem>(class_item, field_name, field_type);
749 
750     // Create foreign method
751     StringItem *method_name = container.GetOrCreateStringItem("ForeignMethod");
752     PrimitiveTypeItem *ret_type = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
753     std::vector<MethodParamItem> params;
754     params.emplace_back(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32));
755     ProtoItem *proto_item = container.GetOrCreateProtoItem(ret_type, params);
756     ForeignMethodItem *method_item = container.CreateItem<ForeignMethodItem>(class_item, method_name, proto_item, 0);
757 
758     MemoryWriter mem_writer;
759 
760     ASSERT_TRUE(container.Write(&mem_writer));
761 
762     // Read panda file from memory
763 
764     auto data = mem_writer.GetData();
765     auto panda_file = GetPandaFile(data);
766 
767     ASSERT_NE(panda_file, nullptr);
768 
769     EXPECT_EQ(panda_file->GetHeader()->foreign_off, class_item->GetOffset());
770 
771     size_t foreign_size = class_item->GetSize() + field_item->GetSize() + method_item->GetSize();
772     EXPECT_EQ(panda_file->GetHeader()->foreign_size, foreign_size);
773 
774     ASSERT_TRUE(panda_file->IsExternal(class_item->GetFileId()));
775 
776     MethodDataAccessor method_data_accessor(*panda_file, method_item->GetFileId());
777     EXPECT_EQ(method_data_accessor.GetMethodId().GetOffset(), method_item->GetOffset());
778     EXPECT_EQ(method_data_accessor.GetSize(), method_item->GetSize());
779     EXPECT_EQ(method_data_accessor.GetClassId().GetOffset(), class_item->GetOffset());
780     EXPECT_EQ(method_data_accessor.GetNameId().GetOffset(), method_name->GetOffset());
781     EXPECT_EQ(method_data_accessor.GetProtoId().GetOffset(), proto_item->GetOffset());
782     EXPECT_TRUE(method_data_accessor.IsExternal());
783 
784     FieldDataAccessor field_data_accessor(*panda_file, field_item->GetFileId());
785     EXPECT_EQ(field_data_accessor.GetFieldId().GetOffset(), field_item->GetOffset());
786     EXPECT_EQ(field_data_accessor.GetSize(), field_item->GetSize());
787     EXPECT_EQ(field_data_accessor.GetClassId().GetOffset(), class_item->GetOffset());
788     EXPECT_EQ(field_data_accessor.GetNameId().GetOffset(), field_name->GetOffset());
789     EXPECT_EQ(field_data_accessor.GetType(), field_type->GetType().GetFieldEncoding());
790     EXPECT_TRUE(field_data_accessor.IsExternal());
791 }
792 
HWTEST(ItemContainer, EmptyContainerChecksum, testing::ext::TestSize.Level0)793 HWTEST(ItemContainer, EmptyContainerChecksum, testing::ext::TestSize.Level0)
794 {
795     using panda::os::file::Mode;
796     using panda::os::file::Open;
797 
798     // Write panda file to disk
799     ItemContainer container;
800 
801     const std::string file_name = "test_empty_checksum.ark";
802     auto writer = FileWriter(file_name);
803 
804     // Initial value of adler32
805     EXPECT_EQ(writer.GetChecksum(), 1U);
806     ASSERT_TRUE(container.Write(&writer));
807 
808     // At least header was written so the checksum should be changed
809     auto container_checksum = writer.GetChecksum();
810     EXPECT_NE(container_checksum, 1U);
811 
812     // Read panda file from disk
813     auto file = File::Open(file_name);
814     EXPECT_NE(file, nullptr);
815     EXPECT_EQ(file->GetHeader()->checksum, container_checksum);
816 
817     constexpr size_t DATA_OFFSET = 12U;
818     auto checksum = adler32(1, file->GetBase() + DATA_OFFSET, file->GetHeader()->file_size - DATA_OFFSET);
819     EXPECT_EQ(file->GetHeader()->checksum, checksum);
820 }
821 
HWTEST(ItemContainer, ContainerChecksum, testing::ext::TestSize.Level0)822 HWTEST(ItemContainer, ContainerChecksum, testing::ext::TestSize.Level0)
823 {
824     using panda::os::file::Mode;
825     using panda::os::file::Open;
826 
827     uint32_t empty_checksum = 0;
828     {
829         ItemContainer container;
830         const std::string file_name = "test_checksum_empty.ark";
831         auto writer = FileWriter(file_name);
832         ASSERT_TRUE(container.Write(&writer));
833         empty_checksum = writer.GetChecksum();
834     }
835     ASSERT(empty_checksum != 0);
836 
837     // Create not empty container
838     ItemContainer container;
839     container.GetOrCreateClassItem("C");
840 
841     const std::string file_name = "test_checksum.ark";
842     auto writer = FileWriter(file_name);
843 
844     ASSERT_TRUE(container.Write(&writer));
845 
846     // This checksum must be different from the empty one (collision may happen though)
847     auto container_checksum = writer.GetChecksum();
848     EXPECT_NE(empty_checksum, container_checksum);
849 
850     // Read panda file from disk
851     auto file = File::Open(file_name);
852     EXPECT_NE(file, nullptr);
853     EXPECT_EQ(file->GetHeader()->checksum, container_checksum);
854 
855     constexpr size_t DATA_OFFSET = 12U;
856     auto checksum = adler32(1, file->GetBase() + DATA_OFFSET, file->GetHeader()->file_size - DATA_OFFSET);
857     EXPECT_EQ(file->GetHeader()->checksum, checksum);
858 }
859 
CreateItems(ItemContainer& container)860 void CreateItems(ItemContainer& container)
861 {
862     // Add classes
863     ClassItem *empty_class_item = container.GetOrCreateClassItem("LTest;");
864     ClassItem *class_item_a = container.GetOrCreateClassItem("LAA;");
865     class_item_a->SetSuperClass(empty_class_item);
866     ClassItem *class_item_b = container.GetOrCreateClassItem("LBB;");
867 
868     // Add method1
869     StringItem *method_name_1 = container.GetOrCreateStringItem("foo1");
870     PrimitiveTypeItem *ret_type_1 = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
871     std::vector<MethodParamItem> params_1;
872     ProtoItem *proto_item_1 = container.GetOrCreateProtoItem(ret_type_1, params_1);
873     MethodItem *method_item_1 = class_item_a->AddMethod(method_name_1, proto_item_1, ACC_PUBLIC | ACC_STATIC, params_1);
874     // Set code_1
875     std::vector<uint8_t> instructions_1 {1, 2, 3, 4};
876     CodeItem *code_item_1 = container.CreateItem<CodeItem>(0, 2, instructions_1);
877     method_item_1->SetCode(code_item_1);
878     code_item_1->AddMethod(method_item_1);
879 
880     // Add method2
881     StringItem *method_name_2 = container.GetOrCreateStringItem("foo2");
882     PrimitiveTypeItem *ret_type_2 = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32);
883     std::vector<MethodParamItem> params_2;
884     ProtoItem *proto_item_2 = container.GetOrCreateProtoItem(ret_type_2, params_2);
885     MethodItem *method_item_2 = class_item_b->AddMethod(method_name_2, proto_item_2, ACC_PUBLIC | ACC_STATIC, params_2);
886     // Set code_2
887     std::vector<uint8_t> instructions_2 {5, 6, 7, 8};
888     CodeItem *code_item_2 = container.CreateItem<CodeItem>(0, 2, instructions_2);
889     method_item_2->SetCode(code_item_2);
890     code_item_2->AddMethod(method_item_2);
891 
892     // Add method_3
893     StringItem *method_name_3 = container.GetOrCreateStringItem("foo3");
894     auto *method_item_3 = empty_class_item->AddMethod(method_name_3, proto_item_1, ACC_PUBLIC | ACC_STATIC, params_1);
895     // Set code_3
896     std::vector<uint8_t> instructions_3 {3, 4, 5, 6};
897     CodeItem *code_item_3 = container.CreateItem<CodeItem>(0, 2, instructions_3);
898     method_item_3->SetCode(code_item_3);
899     code_item_3->AddMethod(method_item_3);
900 
901     // Add method_4
902     StringItem *method_name_4 = container.GetOrCreateStringItem("foo4");
903     auto *method_item_4 = empty_class_item->AddMethod(method_name_4, proto_item_1, ACC_PUBLIC | ACC_STATIC, params_1);
904     // Set code. method_4 and method_3 share code_item_3
905     method_item_4->SetCode(code_item_3);
906     code_item_3->AddMethod(method_item_4);
907 
908     // Add field
909     StringItem *field_name = container.GetOrCreateStringItem("test_field");
910     PrimitiveTypeItem *field_type = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32);
911     class_item_a->AddField(field_name, field_type, ACC_PUBLIC);
912 
913     // Add source file
914     StringItem *source_file = container.GetOrCreateStringItem("source_file");
915     class_item_a->SetSourceFile(source_file);
916 }
917 
CheckItemBeforePGO1(std::list<std::unique_ptr<BaseItem>>::const_iterator& item, std::string_view PRIMITIVE_TYPE_ITEM, std::string_view PROTO_ITEM, std::string_view END_ITEM)918 void CheckItemBeforePGO1(std::list<std::unique_ptr<BaseItem>>::const_iterator& item,
919                          std::string_view PRIMITIVE_TYPE_ITEM,
920                          std::string_view PROTO_ITEM, std::string_view END_ITEM)
921 {
922     EXPECT_EQ((*item)->GetName(), CLASS_ITEM);
923     EXPECT_EQ(panda::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "Test");
924     item++;
925     EXPECT_EQ((*item)->GetName(), CLASS_ITEM);
926     EXPECT_EQ(panda::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "AA");
927     item++;
928     EXPECT_EQ((*item)->GetName(), CLASS_ITEM);
929     EXPECT_EQ(panda::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "BB");
930     item++;
931     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
932     EXPECT_EQ(panda::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo1");
933     item++;
934     EXPECT_EQ((*item)->GetName(), PRIMITIVE_TYPE_ITEM);
935     item++;
936     EXPECT_EQ((*item)->GetName(), PROTO_ITEM);
937     item++;
938     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
939     EXPECT_EQ(panda::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo2");
940     item++;
941     EXPECT_EQ((*item)->GetName(), PRIMITIVE_TYPE_ITEM);
942     item++;
943     EXPECT_EQ((*item)->GetName(), PROTO_ITEM);
944     item++;
945 }
946 
CheckItemBeforePGO2(std::list<std::unique_ptr<BaseItem>>::const_iterator& item, std::string_view PRIMITIVE_TYPE_ITEM, std::string_view PROTO_ITEM, std::string_view END_ITEM)947 void CheckItemBeforePGO2(std::list<std::unique_ptr<BaseItem>>::const_iterator& item,
948                          std::string_view PRIMITIVE_TYPE_ITEM,
949                          std::string_view PROTO_ITEM, std::string_view END_ITEM)
950 {
951     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
952     EXPECT_EQ(panda::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo3");
953     item++;
954     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
955     EXPECT_EQ(panda::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo4");
956     item++;
957     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
958     EXPECT_EQ(panda::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "test_field");
959     item++;
960     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
961     EXPECT_EQ(panda::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "source_file");
962     item++;
963     EXPECT_EQ((*item)->GetName(), END_ITEM);
964     item++;
965     EXPECT_EQ((*item)->GetName(), CODE_ITEM);
966     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[0], "AA::foo1");
967     item++;
968     EXPECT_EQ((*item)->GetName(), CODE_ITEM);
969     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[0], "BB::foo2");
970     item++;
971     EXPECT_EQ((*item)->GetName(), CODE_ITEM);
972     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[0], "Test::foo3");
973     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[1], "Test::foo4");
974     item++;
975     EXPECT_EQ((*item)->GetName(), END_ITEM);
976     item++;
977     EXPECT_EQ((*item)->GetName(), END_ITEM);
978     item++;
979 }
980 
CheckItemAfterPGO1(std::list<std::unique_ptr<BaseItem>>::const_iterator& item, std::string_view PRIMITIVE_TYPE_ITEM, std::string_view PROTO_ITEM, std::string_view END_ITEM)981 void CheckItemAfterPGO1(std::list<std::unique_ptr<BaseItem>>::const_iterator& item,
982                         std::string_view PRIMITIVE_TYPE_ITEM,
983                         std::string_view PROTO_ITEM, std::string_view END_ITEM)
984 {
985     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
986     EXPECT_EQ(panda::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "test_field");
987     item++;
988     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
989     EXPECT_EQ(panda::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo1");
990     item++;
991     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
992     EXPECT_EQ(panda::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo2");
993     item++;
994     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
995     EXPECT_EQ(panda::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo3");
996     item++;
997     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
998     EXPECT_EQ(panda::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "foo4");
999     item++;
1000     EXPECT_EQ((*item)->GetName(), STRING_ITEM);
1001     EXPECT_EQ(panda::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "source_file");
1002     item++;
1003     EXPECT_EQ((*item)->GetName(), CLASS_ITEM);
1004     EXPECT_EQ(panda::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "BB");
1005     item++;
1006     EXPECT_EQ((*item)->GetName(), CLASS_ITEM);
1007     EXPECT_EQ(panda::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "Test");
1008     item++;
1009 }
1010 
CheckItemAfterPGO2(std::list<std::unique_ptr<BaseItem>>::const_iterator& item, std::string_view PRIMITIVE_TYPE_ITEM, std::string_view PROTO_ITEM, std::string_view END_ITEM)1011 void CheckItemAfterPGO2(std::list<std::unique_ptr<BaseItem>>::const_iterator& item,
1012                         std::string_view PRIMITIVE_TYPE_ITEM,
1013                         std::string_view PROTO_ITEM, std::string_view END_ITEM)
1014 {
1015     EXPECT_EQ((*item)->GetName(), CLASS_ITEM);
1016     EXPECT_EQ(panda::panda_file::pgo::ProfileOptimizer::GetNameInfo(*item), "AA");
1017     item++;
1018     EXPECT_EQ((*item)->GetName(), PRIMITIVE_TYPE_ITEM);
1019     item++;
1020     EXPECT_EQ((*item)->GetName(), PROTO_ITEM);
1021     item++;
1022     EXPECT_EQ((*item)->GetName(), PRIMITIVE_TYPE_ITEM);
1023     item++;
1024     EXPECT_EQ((*item)->GetName(), PROTO_ITEM);
1025     item++;
1026     EXPECT_EQ((*item)->GetName(), END_ITEM);
1027     item++;
1028     EXPECT_EQ((*item)->GetName(), END_ITEM);
1029     item++;
1030     EXPECT_EQ((*item)->GetName(), END_ITEM);
1031     item++;
1032     EXPECT_EQ((*item)->GetName(), CODE_ITEM);
1033     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[0], "BB::foo2");
1034     item++;
1035     EXPECT_EQ((*item)->GetName(), CODE_ITEM);
1036     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[0], "Test::foo3");
1037     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[1], "Test::foo4");
1038     item++;
1039     EXPECT_EQ((*item)->GetName(), CODE_ITEM);
1040     EXPECT_EQ(static_cast<CodeItem *>((*item).get())->GetMethodNames()[0], "AA::foo1");
1041     item++;
1042 }
1043 
HWTEST(ItemContainer, TestProfileGuidedRelayout, testing::ext::TestSize.Level0)1044 HWTEST(ItemContainer, TestProfileGuidedRelayout, testing::ext::TestSize.Level0)
1045 {
1046     ItemContainer container;
1047 
1048     // Add
1049     CreateItems(container);
1050 
1051     constexpr std::string_view PRIMITIVE_TYPE_ITEM = "primitive_type_item";
1052     constexpr std::string_view PROTO_ITEM = "proto_item";
1053     constexpr std::string_view END_ITEM = "end_item";
1054 
1055     // Items before PGO
1056     const auto &items = container.GetItems();
1057     auto item = items.begin();
1058     CheckItemBeforePGO1(item, PRIMITIVE_TYPE_ITEM, PROTO_ITEM, END_ITEM);
1059     CheckItemBeforePGO2(item, PRIMITIVE_TYPE_ITEM, PROTO_ITEM, END_ITEM);
1060     EXPECT_EQ(item, items.end());
1061 
1062     // Prepare profile data
1063     std::string profile_path = "TestProfileGuidedRelayout_profile_test_data.txt";
1064     std::ofstream test_file;
1065     test_file.open(profile_path);
1066     test_file << "string_item:test_field" << std::endl;
1067     test_file << "class_item:BB" << std::endl;
1068     test_file << "code_item:BB::foo2" << std::endl;
1069     test_file << "code_item:Test::foo4" << std::endl;
1070     test_file.close();
1071 
1072     // Run PGO
1073     panda::panda_file::pgo::ProfileOptimizer profile_opt;
1074     profile_opt.SetProfilePath(profile_path);
1075     container.ReorderItems(&profile_opt);
1076 
1077     // Items after PGO
1078     item = items.begin();
1079     CheckItemAfterPGO1(item, PRIMITIVE_TYPE_ITEM, PROTO_ITEM, END_ITEM);
1080     CheckItemAfterPGO2(item, PRIMITIVE_TYPE_ITEM, PROTO_ITEM, END_ITEM);
1081     EXPECT_EQ(item, items.end());
1082 }
1083 
PerformTests(ItemContainer& container)1084 void PerformTests(ItemContainer& container)
1085 {
1086     ASSERT_TRUE(container.GetItems().size() == 14);
1087 
1088     std::map<std::string, panda_file::BaseClassItem *> *class_map = container.GetClassMap();
1089     ASSERT_TRUE(class_map != nullptr && class_map->size() == 2);
1090     auto it = class_map->find("Bar");
1091     ASSERT_TRUE(it != class_map->end());
1092 
1093     std::unordered_map<std::string, StringItem *> *string_map = container.GetStringMap();
1094     ASSERT_TRUE(string_map != nullptr && string_map->size() == 4);
1095     auto sit0 = string_map->find("field");
1096     auto sit1 = string_map->find("source_file");
1097     auto sit2 = string_map->find("foo1");
1098     auto sit3 = string_map->find("foo2");
1099     ASSERT_TRUE(sit0 != string_map->end() && sit1 != string_map->end() && sit2 != string_map->end() &&
1100                 sit3 != string_map->end());
1101 
1102     std::unordered_map<Type::TypeId, PrimitiveTypeItem *> *primitive_type_map = container.GetPrimitiveTypeMap();
1103     ASSERT_TRUE(primitive_type_map != nullptr && primitive_type_map->size() == 3);
1104     auto pit0 = primitive_type_map->find(Type::TypeId::F32);
1105     auto pit1 = primitive_type_map->find(Type::TypeId::I32);
1106     auto pit2 = primitive_type_map->find(Type::TypeId::VOID);
1107     ASSERT_TRUE(pit0 != primitive_type_map->end() && pit1 != primitive_type_map->end() &&
1108                 pit2 != primitive_type_map->end());
1109 
1110     auto *rclass_item = static_cast<panda_file::ClassItem *>(it->second);
1111     std::string method_name;
1112     std::function<bool(BaseItem *)> TestMethod = [&](BaseItem *method) {
1113         auto *method_item = static_cast<panda_file::MethodItem *>(method);
1114         ASSERT(method_item != nullptr && method_item->GetItemType() == ItemTypes::METHOD_ITEM);
1115         method_name = method_item->GetNameItem()->GetData();
1116         method_name.pop_back();  // remove '\0'
1117         ASSERT(method_name == "foo1" || method_name == "foo2");
1118         return true;
1119     };
1120 
1121     using std::placeholders::_1;
1122     panda_file::BaseItem::VisitorCallBack cb_method = TestMethod;
1123     rclass_item->VisitMethods(cb_method);
1124 
1125     std::string f_name;
1126     std::function<bool(BaseItem *)> TestField = [&](BaseItem *field) {
1127         auto *field_item = static_cast<panda_file::FieldItem *>(field);
1128         ASSERT(field_item != nullptr && field_item->GetItemType() == ItemTypes::FIELD_ITEM);
1129         f_name = field_item->GetNameItem()->GetData();
1130         f_name.pop_back();  // remove '\0'
1131         ASSERT(f_name == "field");
1132         return true;
1133     };
1134 
1135     panda_file::BaseItem::VisitorCallBack cb_field = TestField;
1136     rclass_item->VisitFields(cb_field);
1137 }
1138 
HWTEST(ItemContainer, GettersTest, testing::ext::TestSize.Level0)1139 HWTEST(ItemContainer, GettersTest, testing::ext::TestSize.Level0)
1140 {
1141     ItemContainer container;
1142 
1143     ClassItem *empty_class_item = container.GetOrCreateClassItem("Foo");
1144 
1145     ClassItem *class_item = container.GetOrCreateClassItem("Bar");
1146     class_item->SetAccessFlags(ACC_PUBLIC);
1147     class_item->SetSuperClass(empty_class_item);
1148 
1149     // Add methods
1150 
1151     StringItem *method_name1 = container.GetOrCreateStringItem("foo1");
1152 
1153     PrimitiveTypeItem *ret_type1 = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
1154     std::vector<MethodParamItem> params1;
1155     params1.emplace_back(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32));
1156     ProtoItem *proto_item1 = container.GetOrCreateProtoItem(ret_type1, params1);
1157 
1158     class_item->AddMethod(method_name1, proto_item1, ACC_PUBLIC | ACC_STATIC, params1);
1159 
1160     StringItem *method_name2 = container.GetOrCreateStringItem("foo2");
1161 
1162     PrimitiveTypeItem *ret_type2 = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32);
1163     std::vector<MethodParamItem> params2;
1164     params2.emplace_back(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::F32));
1165     ProtoItem *proto_item2 = container.GetOrCreateProtoItem(ret_type2, params2);
1166 
1167     class_item->AddMethod(method_name2, proto_item2, ACC_PUBLIC | ACC_STATIC, params2);
1168 
1169     // Add field
1170 
1171     StringItem *field_name = container.GetOrCreateStringItem("field");
1172     PrimitiveTypeItem *field_type = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32);
1173 
1174     class_item->AddField(field_name, field_type, ACC_PUBLIC);
1175 
1176     // Add source file
1177 
1178     StringItem *source_file = container.GetOrCreateStringItem("source_file");
1179 
1180     class_item->SetSourceFile(source_file);
1181 
1182     // Read items from container
1183 
1184     PerformTests(container);
1185 }
1186 
HWTEST(ItemContainer, IndexedItemGlobalIndexTest, testing::ext::TestSize.Level0)1187 HWTEST(ItemContainer, IndexedItemGlobalIndexTest, testing::ext::TestSize.Level0)
1188 {
1189     ItemContainer container;
1190     EXPECT_EQ(container.GetIndexedItemCount(), 0U);
1191 
1192     // Create foreign class
1193     ForeignClassItem *foreign_class_item = container.GetOrCreateForeignClassItem("foreign_class");
1194     EXPECT_EQ(foreign_class_item->GetIndexedItemCount(), 0U);
1195     // BaseClassItem will initialize one StringItem member, which will increase the count by 1.
1196     EXPECT_EQ(container.GetIndexedItemCount(), foreign_class_item->GetIndexedItemCount() + 2);
1197 
1198     // Create foreign field
1199     StringItem *foreign_field_name = container.GetOrCreateStringItem("foreign_field");
1200     PrimitiveTypeItem *foreign_field_type = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32);
1201     ForeignFieldItem *foreign_field_item = container.CreateItem<ForeignFieldItem>(foreign_class_item,
1202         foreign_field_name, foreign_field_type);
1203     EXPECT_EQ(foreign_field_item->GetIndexedItemCount(), 4U);
1204     EXPECT_EQ(container.GetIndexedItemCount(), foreign_field_item->GetIndexedItemCount() + 1);
1205 
1206     // Create foreign method
1207     StringItem *foreign_method_name = container.GetOrCreateStringItem("foreign_method");
1208     PrimitiveTypeItem *foreign_ret_type = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
1209     std::vector<MethodParamItem> foreign_params;
1210     foreign_params.emplace_back(container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I64));
1211     ProtoItem *foreign_proto_item = container.GetOrCreateProtoItem(foreign_ret_type, foreign_params);
1212     ForeignMethodItem *foreign_method_item = container.CreateItem<ForeignMethodItem>(foreign_class_item,
1213         foreign_method_name, foreign_proto_item, 0);
1214     EXPECT_EQ(foreign_method_item->GetIndexedItemCount(), 9U);
1215     EXPECT_EQ(container.GetIndexedItemCount(), foreign_method_item->GetIndexedItemCount() + 1);
1216 
1217     // Create class
1218     ClassItem *class_item = container.GetOrCreateClassItem("classA");
1219     EXPECT_EQ(class_item->GetIndexedItemCount(), 10U);
1220     EXPECT_EQ(container.GetIndexedItemCount(), class_item->GetIndexedItemCount() + 2);
1221 
1222     // Create method
1223     StringItem *method_name = container.GetOrCreateStringItem("a");
1224     // TypeId::VOID is repeated, count won't increase
1225     PrimitiveTypeItem *ret_type = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::VOID);
1226     std::vector<MethodParamItem> params;
1227     ProtoItem *proto_item = container.GetOrCreateProtoItem(ret_type, params);
1228     MethodItem *method_item = class_item->AddMethod(method_name, proto_item, ACC_PUBLIC | ACC_STATIC, params);
1229     EXPECT_EQ(method_item->GetIndexedItemCount(), 14U);
1230     EXPECT_EQ(container.GetIndexedItemCount(), method_item->GetIndexedItemCount() + 1);
1231 
1232     // Create field
1233     StringItem *field_name = container.GetOrCreateStringItem("field");
1234     PrimitiveTypeItem *field_type = container.GetOrCreatePrimitiveTypeItem(Type::TypeId::I32);
1235     FieldItem *field_item = class_item->AddField(field_name, field_type, ACC_PUBLIC);
1236     EXPECT_EQ(field_item->GetIndexedItemCount(), 16U);
1237     EXPECT_EQ(container.GetIndexedItemCount(), field_item->GetIndexedItemCount() + 1);
1238 
1239     // Create code, item count is not expected to increase
1240     std::vector<uint8_t> instructions {1, 2, 3, 4};
1241     CodeItem *code_item = container.CreateItem<CodeItem>(0, 2, instructions);
1242     method_item->SetCode(code_item);
1243     EXPECT_EQ(container.GetIndexedItemCount(), field_item->GetIndexedItemCount() + 1);
1244 
1245     // Create line number program
1246     LineNumberProgramItem *line_number_program_item = container.CreateLineNumberProgramItem();
1247     EXPECT_EQ(line_number_program_item->GetIndexedItemCount(), 17U);
1248     EXPECT_EQ(container.GetIndexedItemCount(), line_number_program_item->GetIndexedItemCount() + 1);
1249 
1250     // Create value items
1251     ScalarValueItem *scalarValueItem = container.CreateItem<ScalarValueItem>(1.0);
1252     EXPECT_EQ(scalarValueItem->GetIndexedItemCount(), 18U);
1253     EXPECT_EQ(container.GetIndexedItemCount(), scalarValueItem->GetIndexedItemCount() + 1);
1254 }
1255 
GenerateModifiedAbc(const std::vector<unsigned char> &buffer, const std::string &filename)1256 void GenerateModifiedAbc(const std::vector<unsigned char> &buffer, const std::string &filename)
1257 {
1258     std::ofstream abc_file(filename, std::ios::out | std::ios::binary);
1259     abc_file.write(reinterpret_cast<const char *>(buffer.data()), buffer.size());
1260     abc_file.close();
1261 }
1262 
HWTEST(ItemContainer, ValidateChecksumTest, testing::ext::TestSize.Level0)1263 HWTEST(ItemContainer, ValidateChecksumTest, testing::ext::TestSize.Level0)
1264 {
1265     using panda::os::file::Mode;
1266     using panda::os::file::Open;
1267 
1268     ItemContainer container;
1269     container.GetOrCreateClassItem("A");
1270 
1271     const std::string file_name = "test_validate_checksum.abc";
1272     auto writer = FileWriter(file_name);
1273 
1274     ASSERT_TRUE(container.Write(&writer));
1275 
1276     auto file = File::Open(file_name);
1277     EXPECT_TRUE(file->ValidateChecksum());
1278 
1279     std::ifstream base_file(file_name, std::ios::binary);
1280     std::vector<unsigned char> buffer(std::istreambuf_iterator<char>(base_file), {});
1281 
1282     std::vector<uint8_t> new_content = {0x01, 0x01};
1283 
1284     // The checksum calculation starts with the 12th element
1285     buffer[12] = new_content[0];
1286     buffer[13] = new_content[1];
1287 
1288     GenerateModifiedAbc(buffer, file_name);
1289 
1290     EXPECT_FALSE(file->ValidateChecksum());
1291 }
1292 
HWTEST(ItemContainer, ThrowIfWithCheckTest, testing::ext::TestSize.Level0)1293 HWTEST(ItemContainer, ThrowIfWithCheckTest, testing::ext::TestSize.Level0)
1294 {
1295     Logger::InitializeStdLogging(Logger::Level::FATAL, Logger::Component::PANDAFILE);
1296     EXPECT_TRUE(Logger::IsLoggingOn(Logger::Level::FATAL, Logger::Component::PANDAFILE));
1297 
1298     using panda::os::file::Mode;
1299     using panda::os::file::Open;
1300 
1301     ItemContainer container;
1302     container.GetOrCreateClassItem("B");
1303 
1304     const std::string file_name = "test_throw_if_with_check.abc";
1305     auto writer = FileWriter(file_name);
1306 
1307     ASSERT_TRUE(container.Write(&writer));
1308 
1309     // Read panda file from disk
1310     auto file = File::Open(file_name);
1311     EXPECT_NE(file, nullptr);
1312 
1313     file->ThrowIfWithCheck(0U > 1U, File::INVALID_FILE_OFFSET);
1314 
1315 #ifdef HOST_UT
1316     EXPECT_DEATH(file->ThrowIfWithCheck(1U > 0U, File::INVALID_FILE_OFFSET),
1317                  "F/pandafile: Invalid file offset");
1318 #endif
1319 
1320     std::ifstream base_file(file_name, std::ios::binary);
1321     std::vector<unsigned char> buffer(std::istreambuf_iterator<char>(base_file), {});
1322 
1323     std::vector<uint8_t> new_content = {0x01, 0x01};
1324 
1325     // The checksum calculation starts with the 12th element
1326     buffer[12] = new_content[0];
1327     buffer[13] = new_content[1];
1328 
1329     GenerateModifiedAbc(buffer, file_name);
1330 
1331 #ifdef HOST_UT
1332     EXPECT_DEATH(file->ThrowIfWithCheck(1U > 0U, File::INVALID_FILE_OFFSET),
1333                  "F/pandafile: Invalid file offset, checksum mismatch. The abc file has been corrupted.");
1334 #endif
1335 
1336 }
1337 }  // namespace panda::panda_file::test
1338