• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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