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 #ifndef STRING_TABLE_BASE_TEST_H 17 #define STRING_TABLE_BASE_TEST_H 18 19 #include "gtest/gtest.h" 20 #include "runtime/include/coretypes/string.h" 21 #include "runtime/include/runtime.h" 22 #include "runtime/include/thread.h" 23 #include "runtime/include/gc_task.h" 24 #include "runtime/include/panda_vm.h" 25 #include "runtime/handle_base-inl.h" 26 #include "runtime/mem/refstorage/global_object_storage.h" 27 #include "runtime/include/thread_scopes.h" 28 29 #include "libpandafile/file.h" 30 #include "libpandafile/file_item_container.h" 31 #include "libpandafile/file_writer.h" 32 33 #include <limits> 34 35 namespace panda::mem::test { 36 class StringTableBaseTest : public testing::Test { 37 public: StringTableBaseTest()38 StringTableBaseTest() {} 39 ~StringTableBaseTest()40 ~StringTableBaseTest() override 41 { 42 Runtime::Destroy(); 43 } 44 45 static coretypes::String *AllocUtf8String(std::vector<uint8_t> data, bool is_movable = true) 46 { 47 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY); 48 return coretypes::String::CreateFromMUtf8(data.data(), utf::MUtf8ToUtf16Size(data.data()), ctx, 49 Runtime::GetCurrent()->GetPandaVM(), is_movable); 50 } 51 SetupRuntime(const std::string & gc_type)52 void SetupRuntime(const std::string &gc_type) 53 { 54 RuntimeOptions options; 55 options.SetShouldLoadBootPandaFiles(false); 56 options.SetShouldInitializeIntrinsics(false); 57 if (gc_type == "g1-gc") { 58 options.SetYoungSpaceSize(1_MB); 59 options.SetHeapSizeLimit(3_MB); 60 } else { 61 options.SetYoungSpaceSize(18_MB); 62 options.SetHeapSizeLimit(36_MB); 63 } 64 65 options.SetGcType(gc_type); 66 options.SetCompilerEnableJit(false); 67 Runtime::Create(options); 68 69 thread_ = panda::MTManagedThread::GetCurrent(); 70 } 71 RunStringTableTests()72 void RunStringTableTests() 73 { 74 EmptyTable(); 75 InternCompressedUtf8AndString(); 76 InternUncompressedUtf8AndString(); 77 InternTheSameUtf16String(); 78 InternManyStrings(); 79 SweepObjectInTable(); 80 InternTooLongString(); 81 } 82 EmptyTable()83 void EmptyTable() 84 { 85 ScopedManagedCodeThread s(thread_); 86 auto table = StringTable(); 87 ASSERT_EQ(table.Size(), 0); 88 } 89 InternCompressedUtf8AndString()90 void InternCompressedUtf8AndString() 91 { 92 ScopedManagedCodeThread s(thread_); 93 auto table = StringTable(); 94 std::vector<uint8_t> data {0x01, 0x02, 0x03, 0x00}; 95 auto *string = AllocUtf8String(data); 96 auto *interned_str1 = 97 table.GetOrInternString(data.data(), data.size() - 1, 98 Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY)); 99 auto *interned_str2 = table.GetOrInternString( 100 string, Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY)); 101 ASSERT_EQ(interned_str1, interned_str2); 102 ASSERT_EQ(table.Size(), 1); 103 } 104 InternUncompressedUtf8AndString()105 void InternUncompressedUtf8AndString() 106 { 107 ScopedManagedCodeThread s(thread_); 108 auto table = StringTable(); 109 std::vector<uint8_t> data {0xc2, 0xa7, 0x34, 0x00}; 110 auto *string = AllocUtf8String(data); 111 auto *interned_str1 = table.GetOrInternString( 112 data.data(), 2, Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY)); 113 auto *interned_str2 = table.GetOrInternString( 114 string, Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY)); 115 ASSERT_EQ(interned_str1, interned_str2); 116 ASSERT_EQ(table.Size(), 1); 117 } 118 InternTheSameUtf16String()119 void InternTheSameUtf16String() 120 { 121 ScopedManagedCodeThread s(thread_); 122 auto table = StringTable(); 123 std::vector<uint16_t> data {0xffc3, 0x33, 0x00}; 124 125 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY); 126 auto *first_string = 127 coretypes::String::CreateFromUtf16(data.data(), data.size(), ctx, Runtime::GetCurrent()->GetPandaVM()); 128 auto *second_string = 129 coretypes::String::CreateFromUtf16(data.data(), data.size(), ctx, Runtime::GetCurrent()->GetPandaVM()); 130 131 auto *interned_str1 = table.GetOrInternString(first_string, ctx); 132 auto *interned_str2 = table.GetOrInternString(second_string, ctx); 133 ASSERT_EQ(interned_str1, interned_str2); 134 ASSERT_EQ(table.Size(), 1); 135 } 136 InternManyStrings()137 void InternManyStrings() 138 { 139 ScopedManagedCodeThread s(thread_); 140 static constexpr size_t iterations = 50; 141 auto table = StringTable(); 142 std::vector<uint8_t> data {0x00}; 143 const unsigned number_of_letters = 25; 144 145 LanguageContext ctx = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY); 146 for (size_t i = 0; i < iterations; i++) { 147 data.insert(data.begin(), (('a' + i) % number_of_letters) + 1); 148 [[maybe_unused]] auto *first_pointer = table.GetOrInternString(AllocUtf8String(data), ctx); 149 [[maybe_unused]] auto *second_pointer = 150 table.GetOrInternString(data.data(), utf::MUtf8ToUtf16Size(data.data()), ctx); 151 auto *third_pointer = table.GetOrInternString(AllocUtf8String(data), ctx); 152 ASSERT_EQ(first_pointer, second_pointer); 153 ASSERT_EQ(second_pointer, third_pointer); 154 } 155 ASSERT_EQ(table.Size(), iterations); 156 } 157 SweepObjectInTable()158 void SweepObjectInTable() 159 { 160 ScopedManagedCodeThread s(thread_); 161 auto table = thread_->GetVM()->GetStringTable(); 162 auto table_init_size = table->Size(); 163 std::vector<uint8_t> data1 {0x01, 0x00}; 164 std::vector<uint8_t> data2 {0x02, 0x00}; 165 std::vector<uint8_t> data3 {0x03, 0x00}; 166 const unsigned EXPECTED_TABLE_SIZE = 2; 167 168 auto storage = thread_->GetVM()->GetGlobalObjectStorage(); 169 170 auto *s1 = AllocUtf8String(data1); 171 auto ref1 = storage->Add(s1, Reference::ObjectType::GLOBAL); 172 auto *s2 = AllocUtf8String(data2); 173 auto ref2 = storage->Add(s2, Reference::ObjectType::GLOBAL); 174 auto *s3 = AllocUtf8String(data3); 175 auto ref3 = storage->Add(s3, Reference::ObjectType::GLOBAL); 176 177 auto panda_class_context = Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY); 178 table->GetOrInternString(reinterpret_cast<coretypes::String *>(storage->Get(ref1)), panda_class_context); 179 table->GetOrInternString(reinterpret_cast<coretypes::String *>(storage->Get(ref2)), panda_class_context); 180 table->GetOrInternString(reinterpret_cast<coretypes::String *>(storage->Get(ref3)), panda_class_context); 181 182 storage->Remove(ref2); 183 184 thread_->GetVM()->GetGC()->WaitForGCInManaged(panda::GCTask(panda::GCTaskCause::EXPLICIT_CAUSE)); 185 // genGC collect all heap for EXPLICIT_CAUSE 186 ASSERT_EQ(table->Size(), table_init_size + EXPECTED_TABLE_SIZE); 187 } 188 InternTooLongString()189 void InternTooLongString() 190 { 191 ScopedManagedCodeThread s(thread_); 192 auto table = StringTable(); 193 auto *runtime = Runtime::GetCurrent(); 194 auto panda_class_context = runtime->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY); 195 196 auto *thread = ManagedThread::GetCurrent(); 197 198 [[maybe_unused]] HandleScope<ObjectHeader *> scope(thread); 199 200 PandaVector<VMHandle<ObjectHeader>> objects; 201 202 std::vector<uint8_t> string_data(10000U, 0x30); 203 string_data.push_back(0x00); 204 205 auto fillHeap = [&string_data, &thread, &objects](bool is_movable) { 206 while (true) { 207 auto *obj = AllocUtf8String(string_data, is_movable); 208 if (obj == nullptr) { 209 thread->ClearException(); 210 break; 211 } 212 objects.emplace_back(thread, obj); 213 } 214 }; 215 216 { 217 fillHeap(true); 218 auto *res = table.GetOrInternString(string_data.data(), string_data.size() - 1, panda_class_context); 219 ASSERT_EQ(res, nullptr); 220 ManagedThread::GetCurrent()->ClearException(); 221 } 222 223 { 224 panda_file::ItemContainer container; 225 panda_file::MemoryWriter writer; 226 227 auto *string_item = container.GetOrCreateStringItem(reinterpret_cast<char *>(string_data.data())); 228 229 container.Write(&writer); 230 auto data = writer.GetData(); 231 232 auto id = panda_file::File::EntityId(string_item->GetOffset()); 233 234 os::mem::ConstBytePtr ptr(reinterpret_cast<std::byte *>(data.data()), data.size(), 235 [](std::byte *, size_t) noexcept {}); 236 237 auto pf = panda_file::File::OpenFromMemory(std::move(ptr)); 238 239 fillHeap(false); 240 auto *res = table.GetOrInternInternalString(*pf.get(), id, panda_class_context); 241 ASSERT_EQ(res, nullptr); 242 ManagedThread::GetCurrent()->ClearException(); 243 } 244 } 245 246 protected: 247 panda::MTManagedThread *thread_; 248 }; 249 } // namespace panda::mem::test 250 251 #endif // STRING_TABLE_BASE_TEST_H