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