1 /*
2 * Copyright (C) 2011 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 #include "intern_table-inl.h"
18
19 #include "base/hash_set.h"
20 #include "common_runtime_test.h"
21 #include "dex/utf.h"
22 #include "gc_root-inl.h"
23 #include "handle_scope-inl.h"
24 #include "mirror/object.h"
25 #include "mirror/string.h"
26 #include "scoped_thread_state_change-inl.h"
27
28 namespace art HIDDEN {
29
30 class InternTableTest : public CommonRuntimeTest {
31 protected:
InternTableTest()32 InternTableTest() {
33 use_boot_image_ = true; // Make the Runtime creation cheaper.
34 }
35 };
36
TEST_F(InternTableTest,Intern)37 TEST_F(InternTableTest, Intern) {
38 ScopedObjectAccess soa(Thread::Current());
39 InternTable intern_table;
40 StackHandleScope<4> hs(soa.Self());
41 Handle<mirror::String> foo_1(hs.NewHandle(intern_table.InternStrong(3, "foo")));
42 Handle<mirror::String> foo_2(hs.NewHandle(intern_table.InternStrong(3, "foo")));
43 Handle<mirror::String> foo_3(
44 hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "foo")));
45 Handle<mirror::String> bar(hs.NewHandle(intern_table.InternStrong(3, "bar")));
46 ASSERT_TRUE(foo_1 != nullptr);
47 ASSERT_TRUE(foo_2 != nullptr);
48 ASSERT_TRUE(foo_3 != nullptr);
49 ASSERT_TRUE(bar != nullptr);
50 EXPECT_EQ(foo_1.Get(), foo_2.Get());
51 EXPECT_TRUE(foo_1->Equals("foo"));
52 EXPECT_TRUE(foo_2->Equals("foo"));
53 EXPECT_TRUE(foo_3->Equals("foo"));
54 EXPECT_NE(foo_1.Get(), bar.Get());
55 EXPECT_NE(foo_2.Get(), bar.Get());
56 EXPECT_NE(foo_3.Get(), bar.Get());
57 }
58
TEST_F(InternTableTest,Size)59 TEST_F(InternTableTest, Size) {
60 ScopedObjectAccess soa(Thread::Current());
61 InternTable t;
62 EXPECT_EQ(0U, t.Size());
63 t.InternStrong(3, "foo");
64 StackHandleScope<1> hs(soa.Self());
65 Handle<mirror::String> foo(
66 hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "foo")));
67 t.InternWeak(foo.Get());
68 EXPECT_EQ(1U, t.Size());
69 t.InternStrong(3, "bar");
70 EXPECT_EQ(2U, t.Size());
71 }
72
73 // Check if table indexes match on 64 and 32 bit machines.
74 // This is done by ensuring hash values are the same on every machine and limited to 32-bit wide.
75 // Otherwise cross compilation can cause a table to be filled on host using one indexing algorithm
76 // and later on a device with different sizeof(size_t) can use another indexing algorithm.
77 // Thus the table may provide wrong data.
TEST_F(InternTableTest,CrossHash)78 TEST_F(InternTableTest, CrossHash) {
79 ScopedObjectAccess soa(Thread::Current());
80 InternTable t;
81
82 // A string that has a negative hash value.
83 ObjPtr<mirror::String> str = mirror::String::AllocFromModifiedUtf8(soa.Self(), "00000000");
84 // `String::GetHashCode()` ensures that the stored hash is calculated.
85 int32_t hash = str->GetHashCode();
86 ASSERT_LT(hash, 0);
87
88 MutexLock mu(Thread::Current(), *Locks::intern_table_lock_);
89 for (InternTable::Table::InternalTable& table : t.strong_interns_.tables_) {
90 // The negative hash value shall be 32-bit wide on every host.
91 ASSERT_TRUE(IsUint<32>(table.set_.hashfn_(GcRoot<mirror::String>(str))));
92 }
93 }
94
95 class TestPredicate : public IsMarkedVisitor {
96 public:
IsMarked(mirror::Object * s)97 mirror::Object* IsMarked(mirror::Object* s) override REQUIRES_SHARED(Locks::mutator_lock_) {
98 bool erased = false;
99 for (auto it = expected_.begin(), end = expected_.end(); it != end; ++it) {
100 if (*it == s) {
101 expected_.erase(it);
102 erased = true;
103 break;
104 }
105 }
106 EXPECT_TRUE(erased);
107 return nullptr;
108 }
109
Expect(const mirror::String * s)110 void Expect(const mirror::String* s) {
111 expected_.push_back(s);
112 }
113
~TestPredicate()114 ~TestPredicate() {
115 EXPECT_EQ(0U, expected_.size());
116 }
117
118 private:
119 mutable std::vector<const mirror::String*> expected_;
120 };
121
TEST_F(InternTableTest,SweepInternTableWeaks)122 TEST_F(InternTableTest, SweepInternTableWeaks) {
123 ScopedObjectAccess soa(Thread::Current());
124 InternTable t;
125 t.InternStrong(3, "foo");
126 t.InternStrong(3, "bar");
127 StackHandleScope<5> hs(soa.Self());
128 Handle<mirror::String> hello(
129 hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello")));
130 Handle<mirror::String> world(
131 hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "world")));
132 Handle<mirror::String> s0(hs.NewHandle(t.InternWeak(hello.Get())));
133 Handle<mirror::String> s1(hs.NewHandle(t.InternWeak(world.Get())));
134
135 EXPECT_EQ(4U, t.Size());
136
137 // We should traverse only the weaks...
138 TestPredicate p;
139 p.Expect(s0.Get());
140 p.Expect(s1.Get());
141 {
142 ReaderMutexLock mu(soa.Self(), *Locks::heap_bitmap_lock_);
143 t.SweepInternTableWeaks(&p);
144 }
145
146 EXPECT_EQ(2U, t.Size());
147
148 // Just check that we didn't corrupt the map.
149 Handle<mirror::String> still_here(
150 hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "still here")));
151 t.InternWeak(still_here.Get());
152 EXPECT_EQ(3U, t.Size());
153 }
154
TEST_F(InternTableTest,ContainsWeak)155 TEST_F(InternTableTest, ContainsWeak) {
156 ScopedObjectAccess soa(Thread::Current());
157 auto ContainsWeak = [&](InternTable& t, ObjPtr<mirror::String> s)
158 REQUIRES_SHARED(Locks::mutator_lock_) {
159 return t.LookupWeak(soa.Self(), s) == s;
160 };
161
162 {
163 // Strongs are never weak.
164 InternTable t;
165 StackHandleScope<2> hs(soa.Self());
166 Handle<mirror::String> interned_foo_1(hs.NewHandle(t.InternStrong(3, "foo")));
167 EXPECT_FALSE(ContainsWeak(t, interned_foo_1.Get()));
168 Handle<mirror::String> interned_foo_2(hs.NewHandle(t.InternStrong(3, "foo")));
169 EXPECT_FALSE(ContainsWeak(t, interned_foo_2.Get()));
170 EXPECT_EQ(interned_foo_1.Get(), interned_foo_2.Get());
171 }
172
173 {
174 // Weaks are always weak.
175 InternTable t;
176 StackHandleScope<4> hs(soa.Self());
177 Handle<mirror::String> foo_1(
178 hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "foo")));
179 Handle<mirror::String> foo_2(
180 hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "foo")));
181 EXPECT_NE(foo_1.Get(), foo_2.Get());
182 Handle<mirror::String> interned_foo_1(hs.NewHandle(t.InternWeak(foo_1.Get())));
183 Handle<mirror::String> interned_foo_2(hs.NewHandle(t.InternWeak(foo_2.Get())));
184 EXPECT_TRUE(ContainsWeak(t, interned_foo_2.Get()));
185 EXPECT_EQ(interned_foo_1.Get(), interned_foo_2.Get());
186 }
187
188 {
189 // A weak can be promoted to a strong.
190 InternTable t;
191 StackHandleScope<3> hs(soa.Self());
192 Handle<mirror::String> foo(
193 hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "foo")));
194 Handle<mirror::String> interned_foo_1(hs.NewHandle(t.InternWeak(foo.Get())));
195 EXPECT_TRUE(ContainsWeak(t, interned_foo_1.Get()));
196 Handle<mirror::String> interned_foo_2(hs.NewHandle(t.InternStrong(3, "foo")));
197 EXPECT_FALSE(ContainsWeak(t, interned_foo_2.Get()));
198 EXPECT_EQ(interned_foo_1.Get(), interned_foo_2.Get());
199 }
200
201 {
202 // Interning a weak after a strong gets you the strong.
203 InternTable t;
204 StackHandleScope<3> hs(soa.Self());
205 Handle<mirror::String> interned_foo_1(hs.NewHandle(t.InternStrong(3, "foo")));
206 EXPECT_FALSE(ContainsWeak(t, interned_foo_1.Get()));
207 Handle<mirror::String> foo(
208 hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "foo")));
209 Handle<mirror::String> interned_foo_2(hs.NewHandle(t.InternWeak(foo.Get())));
210 EXPECT_FALSE(ContainsWeak(t, interned_foo_2.Get()));
211 EXPECT_EQ(interned_foo_1.Get(), interned_foo_2.Get());
212 }
213 }
214
TEST_F(InternTableTest,LookupStrong)215 TEST_F(InternTableTest, LookupStrong) {
216 ScopedObjectAccess soa(Thread::Current());
217 InternTable intern_table;
218 StackHandleScope<3> hs(soa.Self());
219 Handle<mirror::String> foo(hs.NewHandle(intern_table.InternStrong(3, "foo")));
220 Handle<mirror::String> bar(hs.NewHandle(intern_table.InternStrong(3, "bar")));
221 Handle<mirror::String> foobar(hs.NewHandle(intern_table.InternStrong(6, "foobar")));
222 ASSERT_TRUE(foo != nullptr);
223 ASSERT_TRUE(bar != nullptr);
224 ASSERT_TRUE(foobar != nullptr);
225 ASSERT_TRUE(foo->Equals("foo"));
226 ASSERT_TRUE(bar->Equals("bar"));
227 ASSERT_TRUE(foobar->Equals("foobar"));
228 ASSERT_NE(foo.Get(), bar.Get());
229 ASSERT_NE(foo.Get(), foobar.Get());
230 ASSERT_NE(bar.Get(), foobar.Get());
231 ObjPtr<mirror::String> lookup_foo = intern_table.LookupStrong(soa.Self(), 3, "foo");
232 EXPECT_OBJ_PTR_EQ(lookup_foo, foo.Get());
233 ObjPtr<mirror::String> lookup_bar = intern_table.LookupStrong(soa.Self(), 3, "bar");
234 EXPECT_OBJ_PTR_EQ(lookup_bar, bar.Get());
235 ObjPtr<mirror::String> lookup_foobar = intern_table.LookupStrong(soa.Self(), 6, "foobar");
236 EXPECT_OBJ_PTR_EQ(lookup_foobar, foobar.Get());
237 ObjPtr<mirror::String> lookup_foox = intern_table.LookupStrong(soa.Self(), 4, "foox");
238 EXPECT_TRUE(lookup_foox == nullptr);
239 ObjPtr<mirror::String> lookup_fooba = intern_table.LookupStrong(soa.Self(), 5, "fooba");
240 EXPECT_TRUE(lookup_fooba == nullptr);
241 ObjPtr<mirror::String> lookup_foobaR = intern_table.LookupStrong(soa.Self(), 6, "foobaR");
242 EXPECT_TRUE(lookup_foobaR == nullptr);
243 // Try a hash conflict.
244 ASSERT_EQ(ComputeUtf16HashFromModifiedUtf8("foobar", 6),
245 ComputeUtf16HashFromModifiedUtf8("foobbS", 6));
246 ObjPtr<mirror::String> lookup_foobbS = intern_table.LookupStrong(soa.Self(), 6, "foobbS");
247 EXPECT_TRUE(lookup_foobbS == nullptr);
248 }
249
TEST_F(InternTableTest,InternStrongFrozenWeak)250 TEST_F(InternTableTest, InternStrongFrozenWeak) {
251 ScopedObjectAccess soa(Thread::Current());
252 InternTable intern_table;
253 StackHandleScope<1> hs(soa.Self());
254 Handle<mirror::String> foo(
255 hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "foo")));
256 ASSERT_TRUE(foo != nullptr);
257 ObjPtr<mirror::String> weak_foo = intern_table.InternWeak(foo.Get());
258 ASSERT_TRUE(weak_foo == foo.Get());
259
260 intern_table.AddNewTable();
261
262 ObjPtr<mirror::String> strong_foo = intern_table.InternStrong(foo.Get());
263 ASSERT_TRUE(strong_foo == foo.Get());
264 }
265
266 } // namespace art
267