1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef ART_RUNTIME_INTERN_TABLE_INL_H_
18 #define ART_RUNTIME_INTERN_TABLE_INL_H_
19 
20 #include "intern_table.h"
21 
22 #include "dex/utf.h"
23 #include "gc/space/image_space.h"
24 #include "gc_root-inl.h"
25 #include "image.h"
26 #include "mirror/string-inl.h"
27 #include "thread-current-inl.h"
28 
29 namespace art {
30 
operator()31 inline std::size_t InternTable::StringHash::operator()(const GcRoot<mirror::String>& root) const {
32   if (kIsDebugBuild) {
33     Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
34   }
35   // An additional cast to prevent undesired sign extension.
36   return static_cast<size_t>(
37       static_cast<uint32_t>(root.Read<kWithoutReadBarrier>()->GetHashCode()));
38 }
39 
operator()40 inline bool InternTable::StringEquals::operator()(const GcRoot<mirror::String>& a,
41                                                   const GcRoot<mirror::String>& b) const {
42   if (kIsDebugBuild) {
43     Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
44   }
45   return a.Read<kWithoutReadBarrier>()->Equals(b.Read<kWithoutReadBarrier>());
46 }
47 
operator()48 inline bool InternTable::StringEquals::operator()(const GcRoot<mirror::String>& a,
49                                                   const Utf8String& b) const {
50   if (kIsDebugBuild) {
51     Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
52   }
53   ObjPtr<mirror::String> a_string = a.Read<kWithoutReadBarrier>();
54   uint32_t a_length = static_cast<uint32_t>(a_string->GetLength());
55   if (a_length != b.GetUtf16Length()) {
56     return false;
57   }
58   if (a_string->IsCompressed()) {
59     size_t b_byte_count = strlen(b.GetUtf8Data());
60     size_t b_utf8_length = CountModifiedUtf8Chars(b.GetUtf8Data(), b_byte_count);
61     // Modified UTF-8 single byte character range is 0x01 .. 0x7f
62     // The string compression occurs on regular ASCII with same exact range,
63     // not on extended ASCII which up to 0xff
64     const bool is_b_regular_ascii = (b_byte_count == b_utf8_length);
65     if (is_b_regular_ascii) {
66       return memcmp(b.GetUtf8Data(),
67                     a_string->GetValueCompressed(), a_length * sizeof(uint8_t)) == 0;
68     } else {
69       return false;
70     }
71   } else {
72     const uint16_t* a_value = a_string->GetValue();
73     return CompareModifiedUtf8ToUtf16AsCodePointValues(b.GetUtf8Data(), a_value, a_length) == 0;
74   }
75 }
76 
77 template <typename Visitor>
AddImageStringsToTable(gc::space::ImageSpace * image_space,const Visitor & visitor)78 inline void InternTable::AddImageStringsToTable(gc::space::ImageSpace* image_space,
79                                                 const Visitor& visitor) {
80   DCHECK(image_space != nullptr);
81   // Only add if we have the interned strings section.
82   const ImageHeader& header = image_space->GetImageHeader();
83   const ImageSection& section = header.GetInternedStringsSection();
84   if (section.Size() > 0) {
85     AddTableFromMemory(image_space->Begin() + section.Offset(), visitor, !header.IsAppImage());
86   }
87 }
88 
89 template <typename Visitor>
AddTableFromMemory(const uint8_t * ptr,const Visitor & visitor,bool is_boot_image)90 inline size_t InternTable::AddTableFromMemory(const uint8_t* ptr,
91                                               const Visitor& visitor,
92                                               bool is_boot_image) {
93   size_t read_count = 0;
94   UnorderedSet set(ptr, /*make copy*/false, &read_count);
95   {
96     // Hold the lock while calling the visitor to prevent possible race
97     // conditions with another thread adding intern strings.
98     MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
99     // Visit the unordered set, may remove elements.
100     visitor(set);
101     if (!set.empty()) {
102       strong_interns_.AddInternStrings(std::move(set), is_boot_image);
103     }
104   }
105   return read_count;
106 }
107 
AddInternStrings(UnorderedSet && intern_strings,bool is_boot_image)108 inline void InternTable::Table::AddInternStrings(UnorderedSet&& intern_strings,
109                                                  bool is_boot_image) {
110   static constexpr bool kCheckDuplicates = kIsDebugBuild;
111   if (kCheckDuplicates) {
112     // Avoid doing read barriers since the space might not yet be added to the heap.
113     // See b/117803941
114     for (GcRoot<mirror::String>& string : intern_strings) {
115       CHECK(Find(string.Read<kWithoutReadBarrier>()) == nullptr)
116           << "Already found " << string.Read<kWithoutReadBarrier>()->ToModifiedUtf8()
117           << " in the intern table";
118     }
119   }
120   // Insert at the front since we add new interns into the back.
121   tables_.insert(tables_.begin(),
122                  InternalTable(std::move(intern_strings), is_boot_image));
123 }
124 
125 template <typename Visitor>
VisitInterns(const Visitor & visitor,bool visit_boot_images,bool visit_non_boot_images)126 inline void InternTable::VisitInterns(const Visitor& visitor,
127                                       bool visit_boot_images,
128                                       bool visit_non_boot_images) {
129   auto visit_tables = [&](std::vector<Table::InternalTable>& tables)
130       NO_THREAD_SAFETY_ANALYSIS {
131     for (Table::InternalTable& table : tables) {
132       // Determine if we want to visit the table based on the flags..
133       const bool visit =
134           (visit_boot_images && table.IsBootImage()) ||
135           (visit_non_boot_images && !table.IsBootImage());
136       if (visit) {
137         for (auto& intern : table.set_) {
138           visitor(intern);
139         }
140       }
141     }
142   };
143   visit_tables(strong_interns_.tables_);
144   visit_tables(weak_interns_.tables_);
145 }
146 
CountInterns(bool visit_boot_images,bool visit_non_boot_images)147 inline size_t InternTable::CountInterns(bool visit_boot_images,
148                                         bool visit_non_boot_images) const {
149   size_t ret = 0u;
150   auto visit_tables = [&](const std::vector<Table::InternalTable>& tables)
151       NO_THREAD_SAFETY_ANALYSIS {
152     for (const Table::InternalTable& table : tables) {
153       // Determine if we want to visit the table based on the flags..
154       const bool visit =
155           (visit_boot_images && table.IsBootImage()) ||
156           (visit_non_boot_images && !table.IsBootImage());
157       if (visit) {
158         ret += table.set_.size();
159       }
160     }
161   };
162   visit_tables(strong_interns_.tables_);
163   visit_tables(weak_interns_.tables_);
164   return ret;
165 }
166 
167 }  // namespace art
168 
169 #endif  // ART_RUNTIME_INTERN_TABLE_INL_H_
170