• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2024 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 ark {
23 
GetOrInternString(const uint8_t * mutf8Data,uint32_t utf16Length,const LanguageContext & ctx)24 coretypes::String *StringTable::GetOrInternString(const uint8_t *mutf8Data, uint32_t utf16Length,
25                                                   const LanguageContext &ctx)
26 {
27     bool canBeCompressed = coretypes::String::CanBeCompressedMUtf8(mutf8Data);
28     auto *str = internalTable_.GetString(mutf8Data, utf16Length, canBeCompressed, ctx);
29     if (str == nullptr) {
30         str = table_.GetOrInternString(mutf8Data, utf16Length, canBeCompressed, ctx);
31     }
32     return str;
33 }
34 
GetOrInternString(const uint16_t * utf16Data,uint32_t utf16Length,const LanguageContext & ctx)35 coretypes::String *StringTable::GetOrInternString(const uint16_t *utf16Data, uint32_t utf16Length,
36                                                   const LanguageContext &ctx)
37 {
38     auto *str = internalTable_.GetString(utf16Data, utf16Length, ctx);
39     if (str == nullptr) {
40         str = table_.GetOrInternString(utf16Data, utf16Length, 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 = internalTable_.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.utf16Length, data.isAscii, ctx);
62     if (str != nullptr) {
63         table_.PreBarrierOnGet(str);
64         return str;
65     }
66     return internalTable_.GetOrInternString(pf, id, ctx);
67 }
68 
Sweep(const GCObjectVisitor & gcObjectVisitor)69 void StringTable::Sweep(const GCObjectVisitor &gcObjectVisitor)
70 {
71     table_.Sweep(gcObjectVisitor);
72 }
73 
UpdateMoved()74 bool StringTable::UpdateMoved()
75 {
76     return table_.UpdateMoved();
77 }
78 
Size()79 size_t StringTable::Size()
80 {
81     return internalTable_.Size() + table_.Size();
82 }
83 
VisitStrings(const StringVisitor & visitor)84 void StringTable::Table::VisitStrings(const StringVisitor &visitor)
85 {
86     os::memory::ReadLockHolder holder(tableLock_);
87     for (auto entry : table_) {
88         visitor(entry.second);
89     }
90 }
91 
GetString(const uint8_t * utf8Data,uint32_t utf16Length,bool canBeCompressed,const LanguageContext & ctx)92 coretypes::String *StringTable::Table::GetString(const uint8_t *utf8Data, uint32_t utf16Length, bool canBeCompressed,
93                                                  [[maybe_unused]] const LanguageContext &ctx)
94 {
95     uint32_t hashCode = coretypes::String::ComputeHashcodeMutf8(utf8Data, utf16Length, canBeCompressed);
96     os::memory::ReadLockHolder holder(tableLock_);
97     for (auto it = table_.find(hashCode); it != table_.end(); it++) {
98         auto foundString = it->second;
99         if (coretypes::String::StringsAreEqualMUtf8(foundString, utf8Data, utf16Length, canBeCompressed)) {
100             return foundString;
101         }
102     }
103     return nullptr;
104 }
105 
GetString(const uint16_t * utf16Data,uint32_t utf16Length,const LanguageContext & ctx)106 coretypes::String *StringTable::Table::GetString(const uint16_t *utf16Data, uint32_t utf16Length,
107                                                  [[maybe_unused]] const LanguageContext &ctx)
108 {
109     uint32_t hashCode = coretypes::String::ComputeHashcodeUtf16(const_cast<uint16_t *>(utf16Data), utf16Length);
110     os::memory::ReadLockHolder holder(tableLock_);
111     for (auto it = table_.find(hashCode); it != table_.end(); it++) {
112         auto foundString = it->second;
113         if (coretypes::String::StringsAreEqualUtf16(foundString, utf16Data, utf16Length)) {
114             return foundString;
115         }
116     }
117     return nullptr;
118 }
119 
GetString(coretypes::String * string,const LanguageContext & ctx)120 coretypes::String *StringTable::Table::GetString(coretypes::String *string, [[maybe_unused]] const LanguageContext &ctx)
121 {
122     ASSERT(string != nullptr);
123     os::memory::ReadLockHolder holder(tableLock_);
124     auto hash = string->GetHashcode();
125     for (auto it = table_.find(hash); it != table_.end(); it++) {
126         auto foundString = it->second;
127         if (coretypes::String::StringsAreEqual(foundString, string)) {
128             return foundString;
129         }
130     }
131     return nullptr;
132 }
133 
ForceInternString(coretypes::String * string,const LanguageContext & ctx)134 void StringTable::Table::ForceInternString(coretypes::String *string, [[maybe_unused]] const LanguageContext &ctx)
135 {
136     os::memory::WriteLockHolder holder(tableLock_);
137     table_.insert(std::pair<uint32_t, coretypes::String *>(string->GetHashcode(), string));
138 }
139 
InternString(coretypes::String * string,const LanguageContext & ctx)140 coretypes::String *StringTable::Table::InternString(coretypes::String *string,
141                                                     [[maybe_unused]] const LanguageContext &ctx)
142 {
143     ASSERT(string != nullptr);
144     uint32_t hashCode = string->GetHashcode();
145     os::memory::WriteLockHolder holder(tableLock_);
146     // Check string is not present before actually creating and inserting
147     for (auto it = table_.find(hashCode); it != table_.end(); it++) {
148         auto foundString = it->second;
149         if (coretypes::String::StringsAreEqual(foundString, string)) {
150             return foundString;
151         }
152     }
153     table_.insert(std::pair<uint32_t, coretypes::String *>(hashCode, string));
154     return string;
155 }
156 
PreBarrierOnGet(coretypes::String * str)157 void StringTable::Table::PreBarrierOnGet(coretypes::String *str)
158 {
159     // Need pre barrier if string exists in string table, because this string can be got from the
160     // string table (like phoenix) and write to a field during concurrent phase and GC does not see it on Remark
161     ASSERT_MANAGED_CODE();
162     auto *preWrb = Thread::GetCurrent()->GetPreWrbEntrypoint();
163     if (preWrb != nullptr) {
164         reinterpret_cast<mem::ObjRefProcessFunc>(preWrb)(str);
165     }
166 }
167 
GetOrInternString(const uint8_t * mutf8Data,uint32_t utf16Length,bool canBeCompressed,const LanguageContext & ctx)168 coretypes::String *StringTable::Table::GetOrInternString(const uint8_t *mutf8Data, uint32_t utf16Length,
169                                                          bool canBeCompressed, const LanguageContext &ctx)
170 {
171     coretypes::String *result = GetString(mutf8Data, utf16Length, canBeCompressed, ctx);
172     if (result != nullptr) {
173         PreBarrierOnGet(result);
174         return result;
175     }
176 
177     // Even if this string is not inserted, it should get removed during GC
178     result =
179         coretypes::String::CreateFromMUtf8(mutf8Data, utf16Length, canBeCompressed, ctx, Thread::GetCurrent()->GetVM());
180     if (UNLIKELY(result == nullptr)) {
181         return nullptr;
182     }
183     result = InternString(result, ctx);
184 
185     return result;
186 }
187 
GetOrInternString(const uint16_t * utf16Data,uint32_t utf16Length,const LanguageContext & ctx)188 coretypes::String *StringTable::Table::GetOrInternString(const uint16_t *utf16Data, uint32_t utf16Length,
189                                                          const LanguageContext &ctx)
190 {
191     coretypes::String *result = GetString(utf16Data, utf16Length, ctx);
192     if (result != nullptr) {
193         PreBarrierOnGet(result);
194         return result;
195     }
196 
197     // Even if this string is not inserted, it should get removed during GC
198     result = coretypes::String::CreateFromUtf16(utf16Data, utf16Length, ctx, Thread::GetCurrent()->GetVM());
199     if (UNLIKELY(result == nullptr)) {
200         return nullptr;
201     }
202 
203     result = InternString(result, ctx);
204 
205     return result;
206 }
207 
GetOrInternString(coretypes::String * string,const LanguageContext & ctx)208 coretypes::String *StringTable::Table::GetOrInternString(coretypes::String *string, const LanguageContext &ctx)
209 {
210     coretypes::String *result = GetString(string, ctx);
211     if (result != nullptr) {
212         PreBarrierOnGet(result);
213         return result;
214     }
215     result = InternString(string, ctx);
216     return result;
217 }
218 
UpdateMoved()219 bool StringTable::Table::UpdateMoved()
220 {
221     os::memory::WriteLockHolder holder(tableLock_);
222     LOG(DEBUG, GC) << "=== StringTable Update moved. BEGIN ===";
223     LOG(DEBUG, GC) << "Iterate over: " << table_.size() << " elements in string table";
224     bool updated = false;
225     for (auto it = table_.begin(), end = table_.end(); it != end;) {
226         auto *object = it->second;
227         if (object->IsForwarded()) {
228             ObjectHeader *fwdString = ark::mem::GetForwardAddress(object);
229             it->second = static_cast<coretypes::String *>(fwdString);
230             LOG(DEBUG, GC) << "StringTable: forward " << std::hex << object << " -> " << fwdString;
231             updated = true;
232         }
233         ++it;
234     }
235     LOG(DEBUG, GC) << "=== StringTable Update moved. END ===";
236     return updated;
237 }
238 
239 // NOTE(alovkov): make parallel
Sweep(const GCObjectVisitor & gcObjectVisitor)240 void StringTable::Table::Sweep(const GCObjectVisitor &gcObjectVisitor)
241 {
242     os::memory::WriteLockHolder holder(tableLock_);
243     LOG(DEBUG, GC) << "=== StringTable Sweep. BEGIN ===";
244     LOG(DEBUG, GC) << "StringTable iterate over: " << table_.size() << " elements in string table";
245     for (auto it = table_.begin(), end = table_.end(); it != end;) {
246         auto *object = it->second;
247         if (object->IsForwarded()) {
248             ASSERT(gcObjectVisitor(object) != ObjectStatus::DEAD_OBJECT);
249             ObjectHeader *fwdString = ark::mem::GetForwardAddress(object);
250             it->second = static_cast<coretypes::String *>(fwdString);
251             ++it;
252             LOG(DEBUG, GC) << "StringTable: forward " << std::hex << object << " -> " << fwdString;
253         } else if (gcObjectVisitor(object) == ObjectStatus::DEAD_OBJECT) {
254             LOG(DEBUG, GC) << "StringTable: delete string " << std::hex << object
255                            << ", val = " << ConvertToString(object);
256             table_.erase(it++);
257         } else {
258             ++it;
259         }
260     }
261     LOG(DEBUG, GC) << "StringTable size after sweep = " << table_.size();
262     LOG(DEBUG, GC) << "=== StringTable Sweep. END ===";
263 }
264 
Size()265 size_t StringTable::Table::Size()
266 {
267     os::memory::ReadLockHolder holder(tableLock_);
268     return table_.size();
269 }
270 
GetOrInternString(const uint8_t * mutf8Data,uint32_t utf16Length,bool canBeCompressed,const LanguageContext & ctx)271 coretypes::String *StringTable::InternalTable::GetOrInternString(const uint8_t *mutf8Data, uint32_t utf16Length,
272                                                                  bool canBeCompressed, const LanguageContext &ctx)
273 {
274     coretypes::String *result = GetString(mutf8Data, utf16Length, canBeCompressed, ctx);
275     if (result != nullptr) {
276         return result;
277     }
278 
279     result = coretypes::String::CreateFromMUtf8(mutf8Data, utf16Length, canBeCompressed, ctx,
280                                                 Thread::GetCurrent()->GetVM(), false);
281     if (UNLIKELY(result == nullptr)) {
282         return nullptr;
283     }
284     result = InternStringNonMovable(result, ctx);
285     return result;
286 }
287 
GetOrInternString(const uint16_t * utf16Data,uint32_t utf16Length,const LanguageContext & ctx)288 coretypes::String *StringTable::InternalTable::GetOrInternString(const uint16_t *utf16Data, uint32_t utf16Length,
289                                                                  const LanguageContext &ctx)
290 {
291     coretypes::String *result = GetString(utf16Data, utf16Length, ctx);
292     if (result != nullptr) {
293         return result;
294     }
295 
296     result = coretypes::String::CreateFromUtf16(utf16Data, utf16Length, ctx, Thread::GetCurrent()->GetVM(), false);
297     if (UNLIKELY(result == nullptr)) {
298         return nullptr;
299     }
300     result = InternStringNonMovable(result, ctx);
301     return result;
302 }
303 
GetOrInternString(coretypes::String * string,const LanguageContext & ctx)304 coretypes::String *StringTable::InternalTable::GetOrInternString(coretypes::String *string, const LanguageContext &ctx)
305 {
306     coretypes::String *result = GetString(string, ctx);
307     if (result != nullptr) {
308         return result;
309     }
310     result = InternString(string, ctx);
311     return result;
312 }
313 
GetOrInternString(const panda_file::File & pf,panda_file::File::EntityId id,const LanguageContext & ctx)314 coretypes::String *StringTable::InternalTable::GetOrInternString(const panda_file::File &pf,
315                                                                  panda_file::File::EntityId id,
316                                                                  const LanguageContext &ctx)
317 {
318     auto data = pf.GetStringData(id);
319     coretypes::String *result = GetString(data.data, data.utf16Length, data.isAscii, ctx);
320     if (result != nullptr) {
321         return result;
322     }
323     result = coretypes::String::CreateFromMUtf8(data.data, data.utf16Length, data.isAscii, ctx,
324                                                 Thread::GetCurrent()->GetVM(), false);
325     if (UNLIKELY(result == nullptr)) {
326         return nullptr;
327     }
328 
329     result = InternStringNonMovable(result, ctx);
330 
331     // Update cache.
332     os::memory::WriteLockHolder lock(mapsLock_);
333     auto it = maps_.find(&pf);
334     if (it != maps_.end()) {
335         (it->second)[id] = result;
336     } else {
337         PandaUnorderedMap<panda_file::File::EntityId, coretypes::String *, EntityIdEqual> map;
338         map[id] = result;
339         maps_[&pf] = std::move(map);
340     }
341     return result;
342 }
343 
GetStringFast(const panda_file::File & pf,panda_file::File::EntityId id)344 coretypes::String *StringTable::InternalTable::GetStringFast(const panda_file::File &pf, panda_file::File::EntityId id)
345 {
346     os::memory::ReadLockHolder lock(mapsLock_);
347     auto it = maps_.find(&pf);
348     if (it != maps_.end()) {
349         auto idIt = it->second.find(id);
350         if (idIt != it->second.end()) {
351             return idIt->second;
352         }
353     }
354     return nullptr;
355 }
356 
VisitRoots(const StringVisitor & visitor,mem::VisitGCRootFlags flags)357 void StringTable::InternalTable::VisitRoots(const StringVisitor &visitor, mem::VisitGCRootFlags flags)
358 {
359     ASSERT(BitCount(flags & (mem::VisitGCRootFlags::ACCESS_ROOT_ALL | mem::VisitGCRootFlags::ACCESS_ROOT_ONLY_NEW)) ==
360            1);
361 
362     ASSERT(BitCount(flags & (mem::VisitGCRootFlags::START_RECORDING_NEW_ROOT |
363                              mem::VisitGCRootFlags::END_RECORDING_NEW_ROOT)) <= 1);
364     // need to set flags before we iterate, because concurrent allocation should be in proper table
365     if ((flags & mem::VisitGCRootFlags::START_RECORDING_NEW_ROOT) != 0) {
366         os::memory::WriteLockHolder holder(tableLock_);
367         recordNewString_ = true;
368     } else if ((flags & mem::VisitGCRootFlags::END_RECORDING_NEW_ROOT) != 0) {
369         os::memory::WriteLockHolder holder(tableLock_);
370         recordNewString_ = false;
371     }
372 
373     if ((flags & mem::VisitGCRootFlags::ACCESS_ROOT_ALL) != 0) {
374         os::memory::ReadLockHolder lock(tableLock_);
375         for (const auto &v : table_) {
376             visitor(v.second);
377         }
378     } else if ((flags & mem::VisitGCRootFlags::ACCESS_ROOT_ONLY_NEW) != 0) {
379         os::memory::ReadLockHolder lock(tableLock_);
380         for (const auto str : newStringTable_) {
381             visitor(str);
382         }
383     } else {
384         LOG(FATAL, RUNTIME) << "Unknown VisitGCRootFlags: " << static_cast<uint32_t>(flags);
385     }
386     if ((flags & mem::VisitGCRootFlags::END_RECORDING_NEW_ROOT) != 0) {
387         os::memory::WriteLockHolder holder(tableLock_);
388         newStringTable_.clear();
389     }
390 }
391 
InternStringNonMovable(coretypes::String * string,const LanguageContext & ctx)392 coretypes::String *StringTable::InternalTable::InternStringNonMovable(coretypes::String *string,
393                                                                       const LanguageContext &ctx)
394 {
395     auto *result = InternString(string, ctx);
396     os::memory::WriteLockHolder holder(tableLock_);
397     if (recordNewString_) {
398         newStringTable_.push_back(result);
399     }
400     return result;
401 }
402 
403 }  // namespace ark
404