1 /*
2 * Copyright (c) 2021 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 "gtest/gtest.h"
17 #include "runtime/include/coretypes/string.h"
18 #include "runtime/include/runtime.h"
19 #include "runtime/include/thread.h"
20 #include "runtime/include/gc_task.h"
21 #include "runtime/include/panda_vm.h"
22 #include "runtime/handle_base-inl.h"
23
24 namespace panda::mem::test {
25
26 class StringTableTest : public testing::Test {
27 public:
StringTableTest()28 StringTableTest()
29 {
30 RuntimeOptions options;
31 options.SetShouldLoadBootPandaFiles(false);
32 options.SetShouldInitializeIntrinsics(false);
33
34 options.SetCompilerEnableJit(false);
35 Runtime::Create(options);
36 }
37
~StringTableTest()38 ~StringTableTest() override
39 {
40 Runtime::Destroy();
41 }
42
AllocUtf8String(std::vector<uint8_t> data)43 static coretypes::String *AllocUtf8String(std::vector<uint8_t> data)
44 {
45 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
46 return coretypes::String::CreateFromMUtf8(data.data(), utf::MUtf8ToUtf16Size(data.data()), ctx,
47 Runtime::GetCurrent()->GetPandaVM());
48 }
49
SetUp()50 void SetUp() override
51 {
52 thread_ = panda::MTManagedThread::GetCurrent();
53 thread_->ManagedCodeBegin();
54 }
55
TearDown()56 void TearDown() override
57 {
58 thread_->ManagedCodeEnd();
59 }
60
61 protected:
62 panda::MTManagedThread *thread_ {nullptr};
63 };
64
TEST_F(StringTableTest,EmptyTable)65 TEST_F(StringTableTest, EmptyTable)
66 {
67 auto table = StringTable();
68 ASSERT_EQ(table.Size(), 0);
69 }
70
TEST_F(StringTableTest,InternCompressedUtf8AndString)71 TEST_F(StringTableTest, InternCompressedUtf8AndString)
72 {
73 auto table = StringTable();
74 std::vector<uint8_t> data {0x01, 0x02, 0x03, 0x00};
75 auto *string = AllocUtf8String(data);
76 auto *interned_str1 =
77 table.GetOrInternString(data.data(), data.size() - 1,
78 Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY));
79 auto *interned_str2 = table.GetOrInternString(
80 string, Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY));
81 ASSERT_EQ(interned_str1, interned_str2);
82 ASSERT_EQ(table.Size(), 1);
83 }
84
TEST_F(StringTableTest,InternUncompressedUtf8AndString)85 TEST_F(StringTableTest, InternUncompressedUtf8AndString)
86 {
87 auto table = StringTable();
88 std::vector<uint8_t> data {0xc2, 0xa7, 0x34, 0x00};
89 auto *string = AllocUtf8String(data);
90 auto *interned_str1 = table.GetOrInternString(
91 data.data(), 2, Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY));
92 auto *interned_str2 = table.GetOrInternString(
93 string, Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY));
94 ASSERT_EQ(interned_str1, interned_str2);
95 ASSERT_EQ(table.Size(), 1);
96 }
97
TEST_F(StringTableTest,InternTheSameUtf16String)98 TEST_F(StringTableTest, InternTheSameUtf16String)
99 {
100 auto table = StringTable();
101 std::vector<uint16_t> data {0xffc3, 0x33, 0x00};
102
103 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
104 auto *first_string =
105 coretypes::String::CreateFromUtf16(data.data(), data.size(), ctx, Runtime::GetCurrent()->GetPandaVM());
106 auto *second_string =
107 coretypes::String::CreateFromUtf16(data.data(), data.size(), ctx, Runtime::GetCurrent()->GetPandaVM());
108
109 auto *interned_str1 = table.GetOrInternString(first_string, ctx);
110 auto *interned_str2 = table.GetOrInternString(second_string, ctx);
111 ASSERT_EQ(interned_str1, interned_str2);
112 ASSERT_EQ(table.Size(), 1);
113 }
114
TEST_F(StringTableTest,InternManyStrings)115 TEST_F(StringTableTest, InternManyStrings)
116 {
117 static constexpr size_t ITERATIONS = 50;
118 auto table = StringTable();
119 std::vector<uint8_t> data {0x00};
120
121 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
122 for (size_t i = 0; i < ITERATIONS; i++) {
123 data.insert(data.begin(), (('a' + i) % 25) + 1);
124 [[maybe_unused]] auto *first_pointer = table.GetOrInternString(AllocUtf8String(data), ctx);
125 [[maybe_unused]] auto *second_pointer =
126 table.GetOrInternString(data.data(), utf::MUtf8ToUtf16Size(data.data()), ctx);
127 auto *third_pointer = table.GetOrInternString(AllocUtf8String(data), ctx);
128 ASSERT_EQ(first_pointer, second_pointer);
129 ASSERT_EQ(second_pointer, third_pointer);
130 }
131 ASSERT_EQ(table.Size(), ITERATIONS);
132 }
133
TEST_F(StringTableTest,SweepObjectInTable)134 TEST_F(StringTableTest, SweepObjectInTable)
135 {
136 auto table = thread_->GetVM()->GetStringTable();
137 auto table_init_size = table->Size();
138 std::vector<uint8_t> data1 {0x01, 0x00};
139 std::vector<uint8_t> data2 {0x02, 0x00};
140 std::vector<uint8_t> data3 {0x03, 0x00};
141 auto *s1 = AllocUtf8String(data1);
142 auto *s2 = AllocUtf8String(data2);
143 auto *s3 = AllocUtf8String(data3);
144 auto thread = ManagedThread::GetCurrent();
145 [[maybe_unused]] HandleScope<ObjectHeader *> scope(thread);
146 VMHandle<coretypes::String> s2h(thread, s2);
147 VMHandle<coretypes::String> s3h(thread, s3);
148 table->GetOrInternString(s1, Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY));
149 table->GetOrInternString(s2, Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY));
150 table->GetOrInternString(s3, Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY));
151 s1->SetMarkedForGC();
152 s3->SetMarkedForGC();
153 ASSERT_FALSE(s2->IsMarkedForGC());
154 thread_->GetVM()->GetGC()->WaitForGCInManaged(panda::GCTask(panda::GCTaskCause::EXPLICIT_CAUSE));
155 // There is no guarantee that Tenured GC will be called - so GE instead of EQ
156 ASSERT_GE(table->Size(), table_init_size + 2);
157 }
158
159 } // namespace panda::mem::test
160