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