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