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 "runtime/string_table.h"
17
18 #include "runtime/include/relayout_profiler.h"
19 #include "runtime/include/runtime.h"
20 #include "runtime/mem/object_helpers.h"
21
22 namespace panda {
23
GetOrInternString(const uint8_t * mutf8_data,uint32_t utf16_length,const LanguageContext & ctx)24 coretypes::String *StringTable::GetOrInternString(const uint8_t *mutf8_data, uint32_t utf16_length,
25 const LanguageContext &ctx)
26 {
27 bool can_be_compressed = coretypes::String::CanBeCompressedMUtf8(mutf8_data);
28 auto *str = internal_table_.GetString(mutf8_data, utf16_length, can_be_compressed, ctx);
29 if (str == nullptr) {
30 str = table_.GetOrInternString(mutf8_data, utf16_length, can_be_compressed, ctx);
31 }
32 return str;
33 }
34
GetOrInternString(const uint16_t * utf16_data,uint32_t utf16_length,const LanguageContext & ctx)35 coretypes::String *StringTable::GetOrInternString(const uint16_t *utf16_data, uint32_t utf16_length,
36 const LanguageContext &ctx)
37 {
38 auto *str = internal_table_.GetString(utf16_data, utf16_length, ctx);
39 if (str == nullptr) {
40 str = table_.GetOrInternString(utf16_data, utf16_length, ctx);
41 }
42 return str;
43 }
44
GetOrInternString(coretypes::String * string,const LanguageContext & ctx)45 coretypes::String *StringTable::GetOrInternString(coretypes::String *string, const LanguageContext &ctx)
46 {
47 auto *str = internal_table_.GetString(string, ctx);
48 if (str == nullptr) {
49 str = table_.GetOrInternString(string, ctx);
50 }
51 return str;
52 }
53
GetOrInternInternalString(const panda_file::File & pf,panda_file::File::EntityId id,const LanguageContext & ctx)54 coretypes::String *StringTable::GetOrInternInternalString(const panda_file::File &pf, panda_file::File::EntityId id,
55 const LanguageContext &ctx)
56 {
57 auto data = pf.GetStringData(id);
58
59 ADD_PROFILE_STRING_ITEM(pf.GetFilename(), utf::Mutf8AsCString(data.data));
60
61 coretypes::String *str = table_.GetString(data.data, data.utf16_length, data.is_ascii, ctx);
62 if (str != nullptr) {
63 return str;
64 }
65 return internal_table_.GetOrInternString(pf, id, ctx);
66 }
67
Sweep(const GCObjectVisitor & gc_object_visitor)68 void StringTable::Sweep(const GCObjectVisitor &gc_object_visitor)
69 {
70 table_.Sweep(gc_object_visitor);
71 }
72
UpdateMoved()73 bool StringTable::UpdateMoved()
74 {
75 return table_.UpdateMoved();
76 }
77
Size()78 size_t StringTable::Size()
79 {
80 return internal_table_.Size() + table_.Size();
81 }
82
VisitStrings(const StringVisitor & visitor)83 void StringTable::Table::VisitStrings(const StringVisitor &visitor)
84 {
85 os::memory::ReadLockHolder holder(table_lock_);
86 for (auto entry : table_) {
87 visitor(entry.second);
88 }
89 }
90
GetString(const uint8_t * utf8_data,uint32_t utf16_length,bool can_be_compressed,const LanguageContext & ctx)91 coretypes::String *StringTable::Table::GetString(const uint8_t *utf8_data, uint32_t utf16_length,
92 bool can_be_compressed, [[maybe_unused]] const LanguageContext &ctx)
93 {
94 uint32_t hash_code = coretypes::String::ComputeHashcodeMutf8(utf8_data, utf16_length, can_be_compressed);
95 os::memory::ReadLockHolder holder(table_lock_);
96 for (auto it = table_.find(hash_code); it != table_.end(); it++) {
97 auto found_string = it->second;
98 if (coretypes::String::StringsAreEqualMUtf8(found_string, utf8_data, utf16_length, can_be_compressed)) {
99 return found_string;
100 }
101 }
102 return nullptr;
103 }
104
GetString(const uint16_t * utf16_data,uint32_t utf16_length,const LanguageContext & ctx)105 coretypes::String *StringTable::Table::GetString(const uint16_t *utf16_data, uint32_t utf16_length,
106 [[maybe_unused]] const LanguageContext &ctx)
107 {
108 uint32_t hash_code = coretypes::String::ComputeHashcodeUtf16(const_cast<uint16_t *>(utf16_data), utf16_length);
109 os::memory::ReadLockHolder holder(table_lock_);
110 for (auto it = table_.find(hash_code); it != table_.end(); it++) {
111 auto found_string = it->second;
112 if (coretypes::String::StringsAreEqualUtf16(found_string, utf16_data, utf16_length)) {
113 return found_string;
114 }
115 }
116 return nullptr;
117 }
118
GetString(coretypes::String * string,const LanguageContext & ctx)119 coretypes::String *StringTable::Table::GetString(coretypes::String *string, [[maybe_unused]] const LanguageContext &ctx)
120 {
121 ASSERT(string != nullptr);
122 os::memory::ReadLockHolder holder(table_lock_);
123 auto hash = string->GetHashcode();
124 for (auto it = table_.find(hash); it != table_.end(); it++) {
125 auto found_string = it->second;
126 if (coretypes::String::StringsAreEqual(found_string, string)) {
127 return found_string;
128 }
129 }
130 return nullptr;
131 }
132
ForceInternString(coretypes::String * string,const LanguageContext & ctx)133 void StringTable::Table::ForceInternString(coretypes::String *string, [[maybe_unused]] const LanguageContext &ctx)
134 {
135 os::memory::WriteLockHolder holder(table_lock_);
136 table_.insert(std::pair<uint32_t, coretypes::String *>(string->GetHashcode(), string));
137 }
138
InternString(coretypes::String * string,const LanguageContext & ctx)139 coretypes::String *StringTable::Table::InternString(coretypes::String *string,
140 [[maybe_unused]] const LanguageContext &ctx)
141 {
142 ASSERT(string != nullptr);
143 uint32_t hash_code = string->GetHashcode();
144 os::memory::WriteLockHolder holder(table_lock_);
145 // Check string is not present before actually creating and inserting
146 for (auto it = table_.find(hash_code); it != table_.end(); it++) {
147 auto found_string = it->second;
148 if (coretypes::String::StringsAreEqual(found_string, string)) {
149 return found_string;
150 }
151 }
152 table_.insert(std::pair<uint32_t, coretypes::String *>(hash_code, string));
153 return string;
154 }
155
GetOrInternString(const uint8_t * mutf8_data,uint32_t utf16_length,bool can_be_compressed,const LanguageContext & ctx)156 coretypes::String *StringTable::Table::GetOrInternString(const uint8_t *mutf8_data, uint32_t utf16_length,
157 bool can_be_compressed, const LanguageContext &ctx)
158 {
159 coretypes::String *result = GetString(mutf8_data, utf16_length, can_be_compressed, ctx);
160 if (result != nullptr) {
161 return result;
162 }
163
164 // Even if this string is not inserted, it should get removed during GC
165 result = coretypes::String::CreateFromMUtf8(mutf8_data, utf16_length, can_be_compressed, ctx,
166 Thread::GetCurrent()->GetVM());
167 if (UNLIKELY(result == nullptr)) {
168 return nullptr;
169 }
170 result = InternString(result, ctx);
171
172 return result;
173 }
174
GetOrInternString(const uint16_t * utf16_data,uint32_t utf16_length,const LanguageContext & ctx)175 coretypes::String *StringTable::Table::GetOrInternString(const uint16_t *utf16_data, uint32_t utf16_length,
176 const LanguageContext &ctx)
177 {
178 coretypes::String *result = GetString(utf16_data, utf16_length, ctx);
179 if (result != nullptr) {
180 return result;
181 }
182
183 // Even if this string is not inserted, it should get removed during GC
184 result = coretypes::String::CreateFromUtf16(utf16_data, utf16_length, ctx, Thread::GetCurrent()->GetVM());
185 if (UNLIKELY(result == nullptr)) {
186 return nullptr;
187 }
188
189 result = InternString(result, ctx);
190
191 return result;
192 }
193
GetOrInternString(coretypes::String * string,const LanguageContext & ctx)194 coretypes::String *StringTable::Table::GetOrInternString(coretypes::String *string, const LanguageContext &ctx)
195 {
196 coretypes::String *result = GetString(string, ctx);
197 if (result != nullptr) {
198 return result;
199 }
200 result = InternString(string, ctx);
201 return result;
202 }
203
UpdateMoved()204 bool StringTable::Table::UpdateMoved()
205 {
206 os::memory::WriteLockHolder holder(table_lock_);
207 LOG(DEBUG, GC) << "=== StringTable Update moved. BEGIN ===";
208 LOG(DEBUG, GC) << "Iterate over: " << table_.size() << " elements in string table";
209 bool updated = false;
210 for (auto it = table_.begin(), end = table_.end(); it != end;) {
211 auto *object = it->second;
212 if (object->IsForwarded()) {
213 ObjectHeader *fwd_string = panda::mem::GetForwardAddress(object);
214 it->second = static_cast<coretypes::String *>(fwd_string);
215 LOG(DEBUG, GC) << "StringTable: forward " << std::hex << object << " -> " << fwd_string;
216 updated = true;
217 }
218 ++it;
219 }
220 LOG(DEBUG, GC) << "=== StringTable Update moved. END ===";
221 return updated;
222 }
223
224 // TODO(alovkov): make parallel
Sweep(const GCObjectVisitor & gc_object_visitor)225 void StringTable::Table::Sweep(const GCObjectVisitor &gc_object_visitor)
226 {
227 os::memory::WriteLockHolder holder(table_lock_);
228 LOG(DEBUG, GC) << "=== StringTable Sweep. BEGIN ===";
229 LOG(DEBUG, GC) << "StringTable iterate over: " << table_.size() << " elements in string table";
230 for (auto it = table_.begin(), end = table_.end(); it != end;) {
231 auto *object = it->second;
232 if (object->IsForwarded()) {
233 ASSERT(gc_object_visitor(object) != ObjectStatus::DEAD_OBJECT);
234 ObjectHeader *fwd_string = panda::mem::GetForwardAddress(object);
235 it->second = static_cast<coretypes::String *>(fwd_string);
236 ++it;
237 LOG(DEBUG, GC) << "StringTable: forward " << std::hex << object << " -> " << fwd_string;
238 } else if (gc_object_visitor(object) == ObjectStatus::DEAD_OBJECT) {
239 LOG(DEBUG, GC) << "StringTable: delete string " << std::hex << object
240 << ", val = " << ConvertToString(object);
241 table_.erase(it++);
242 } else {
243 ++it;
244 }
245 }
246 LOG(DEBUG, GC) << "StringTable size after sweep = " << table_.size();
247 LOG(DEBUG, GC) << "=== StringTable Sweep. END ===";
248 }
249
Size()250 size_t StringTable::Table::Size()
251 {
252 os::memory::ReadLockHolder holder(table_lock_);
253 return table_.size();
254 }
255
GetOrInternString(const uint8_t * mutf8_data,uint32_t utf16_length,bool can_be_compressed,const LanguageContext & ctx)256 coretypes::String *StringTable::InternalTable::GetOrInternString(const uint8_t *mutf8_data, uint32_t utf16_length,
257 bool can_be_compressed, const LanguageContext &ctx)
258 {
259 coretypes::String *result = GetString(mutf8_data, utf16_length, can_be_compressed, ctx);
260 if (result != nullptr) {
261 return result;
262 }
263
264 result = coretypes::String::CreateFromMUtf8(mutf8_data, utf16_length, can_be_compressed, ctx,
265 Thread::GetCurrent()->GetVM(), false);
266 if (UNLIKELY(result == nullptr)) {
267 return nullptr;
268 }
269 result = InternStringNonMovable(result, ctx);
270 return result;
271 }
272
GetOrInternString(const uint16_t * utf16_data,uint32_t utf16_length,const LanguageContext & ctx)273 coretypes::String *StringTable::InternalTable::GetOrInternString(const uint16_t *utf16_data, uint32_t utf16_length,
274 const LanguageContext &ctx)
275 {
276 coretypes::String *result = GetString(utf16_data, utf16_length, ctx);
277 if (result != nullptr) {
278 return result;
279 }
280
281 result = coretypes::String::CreateFromUtf16(utf16_data, utf16_length, ctx, Thread::GetCurrent()->GetVM(), false);
282 if (UNLIKELY(result == nullptr)) {
283 return nullptr;
284 }
285 result = InternStringNonMovable(result, ctx);
286 return result;
287 }
288
GetOrInternString(const panda_file::File & pf,panda_file::File::EntityId id,const LanguageContext & ctx)289 coretypes::String *StringTable::InternalTable::GetOrInternString(const panda_file::File &pf,
290 panda_file::File::EntityId id,
291 const LanguageContext &ctx)
292 {
293 auto data = pf.GetStringData(id);
294 coretypes::String *result = GetString(data.data, data.utf16_length, data.is_ascii, ctx);
295 if (result != nullptr) {
296 return result;
297 }
298 result = coretypes::String::CreateFromMUtf8(data.data, data.utf16_length, data.is_ascii, ctx,
299 Thread::GetCurrent()->GetVM(), false);
300 if (UNLIKELY(result == nullptr)) {
301 return nullptr;
302 }
303
304 result = InternStringNonMovable(result, ctx);
305
306 // Update cache.
307 os::memory::WriteLockHolder lock(maps_lock_);
308 auto it = maps_.find(&pf);
309 if (it != maps_.end()) {
310 (it->second)[id] = result;
311 } else {
312 PandaUnorderedMap<panda_file::File::EntityId, coretypes::String *, EntityIdEqual> map;
313 map[id] = result;
314 maps_[&pf] = std::move(map);
315 }
316 return result;
317 }
318
GetStringFast(const panda_file::File & pf,panda_file::File::EntityId id)319 coretypes::String *StringTable::InternalTable::GetStringFast(const panda_file::File &pf, panda_file::File::EntityId id)
320 {
321 os::memory::ReadLockHolder lock(maps_lock_);
322 auto it = maps_.find(&pf);
323 if (it != maps_.end()) {
324 auto id_it = it->second.find(id);
325 if (id_it != it->second.end()) {
326 return id_it->second;
327 }
328 }
329 return nullptr;
330 }
331
VisitRoots(const StringVisitor & visitor,mem::VisitGCRootFlags flags)332 void StringTable::InternalTable::VisitRoots(const StringVisitor &visitor, mem::VisitGCRootFlags flags)
333 {
334 ASSERT(BitCount(flags & (mem::VisitGCRootFlags::ACCESS_ROOT_ALL | mem::VisitGCRootFlags::ACCESS_ROOT_ONLY_NEW)) ==
335 1);
336
337 ASSERT(BitCount(flags & (mem::VisitGCRootFlags::START_RECORDING_NEW_ROOT |
338 mem::VisitGCRootFlags::END_RECORDING_NEW_ROOT)) <= 1);
339 // need to set flags before we iterate, because concurrent allocation should be in proper table
340 if ((flags & mem::VisitGCRootFlags::START_RECORDING_NEW_ROOT) != 0) {
341 os::memory::WriteLockHolder holder(table_lock_);
342 record_new_string_ = true;
343 } else if ((flags & mem::VisitGCRootFlags::END_RECORDING_NEW_ROOT) != 0) {
344 os::memory::WriteLockHolder holder(table_lock_);
345 record_new_string_ = false;
346 }
347
348 if ((flags & mem::VisitGCRootFlags::ACCESS_ROOT_ALL) != 0) {
349 os::memory::ReadLockHolder lock(table_lock_);
350 for (const auto &v : table_) {
351 visitor(v.second);
352 }
353 } else if ((flags & mem::VisitGCRootFlags::ACCESS_ROOT_ONLY_NEW) != 0) {
354 os::memory::ReadLockHolder lock(table_lock_);
355 for (const auto str : new_string_table_) {
356 visitor(str);
357 }
358 } else {
359 LOG(FATAL, RUNTIME) << "Unknown VisitGCRootFlags: " << static_cast<uint32_t>(flags);
360 }
361 if ((flags & mem::VisitGCRootFlags::END_RECORDING_NEW_ROOT) != 0) {
362 os::memory::WriteLockHolder holder(table_lock_);
363 new_string_table_.clear();
364 }
365 }
366
InternStringNonMovable(coretypes::String * string,const LanguageContext & ctx)367 coretypes::String *StringTable::InternalTable::InternStringNonMovable(coretypes::String *string,
368 const LanguageContext &ctx)
369 {
370 auto *result = InternString(string, ctx);
371 os::memory::WriteLockHolder holder(table_lock_);
372 if (record_new_string_) {
373 new_string_table_.push_back(result);
374 }
375 return result;
376 }
377
378 } // namespace panda
379