| /* | |
| * Copyright (C) 2016 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| #include "class_table-inl.h" | |
| #include "art_field-inl.h" | |
| #include "art_method-inl.h" | |
| #include "class_linker-inl.h" | |
| #include "common_runtime_test.h" | |
| #include "dex/dex_file.h" | |
| #include "gc/accounting/card_table-inl.h" | |
| #include "gc/heap.h" | |
| #include "handle_scope-inl.h" | |
| #include "mirror/class-alloc-inl.h" | |
| #include "obj_ptr.h" | |
| #include "scoped_thread_state_change-inl.h" | |
| namespace art HIDDEN { | |
| namespace mirror { | |
| class CollectRootVisitor { | |
| public: | |
| CollectRootVisitor() {} | |
| template <class MirrorType> | |
| ALWAYS_INLINE void VisitRootIfNonNull(GcRoot<MirrorType>& root) const | |
| REQUIRES_SHARED(Locks::mutator_lock_) { | |
| if (!root.IsNull()) { | |
| VisitRoot(root); | |
| } | |
| } | |
| template <class MirrorType> | |
| ALWAYS_INLINE void VisitRootIfNonNull(mirror::CompressedReference<MirrorType>* root) const | |
| REQUIRES_SHARED(Locks::mutator_lock_) { | |
| if (!root->IsNull()) { | |
| VisitRoot(root); | |
| } | |
| } | |
| template <class MirrorType> | |
| void VisitRoot(GcRoot<MirrorType>& root) const REQUIRES_SHARED(Locks::mutator_lock_) { | |
| VisitRoot(root.AddressWithoutBarrier()); | |
| } | |
| template <class MirrorType> | |
| void VisitRoot(mirror::CompressedReference<MirrorType>* root) const | |
| REQUIRES_SHARED(Locks::mutator_lock_) { | |
| roots_.insert(root->AsMirrorPtr()); | |
| } | |
| mutable std::set<mirror::Object*> roots_; | |
| }; | |
| class ClassTableTest : public CommonRuntimeTest { | |
| protected: | |
| ClassTableTest() { | |
| use_boot_image_ = true; // Make the Runtime creation cheaper. | |
| } | |
| }; | |
| TEST_F(ClassTableTest, ClassTable) { | |
| ScopedObjectAccess soa(Thread::Current()); | |
| jobject jclass_loader = LoadDex("XandY"); | |
| VariableSizedHandleScope hs(soa.Self()); | |
| Handle<ClassLoader> class_loader(hs.NewHandle(soa.Decode<ClassLoader>(jclass_loader))); | |
| const char* descriptor_x = "LX;"; | |
| const char* descriptor_y = "LY;"; | |
| Handle<mirror::Class> h_X = hs.NewHandle(FindClass(descriptor_x, class_loader)); | |
| Handle<mirror::Class> h_Y = hs.NewHandle(FindClass(descriptor_y, class_loader)); | |
| Handle<mirror::Object> obj_X = hs.NewHandle(h_X->AllocObject(soa.Self())); | |
| ASSERT_TRUE(obj_X != nullptr); | |
| ClassTable table; | |
| EXPECT_EQ(table.NumZygoteClasses(class_loader.Get()), 0u); | |
| EXPECT_EQ(table.NumNonZygoteClasses(class_loader.Get()), 0u); | |
| // Add h_X to the class table. | |
| table.Insert(h_X.Get()); | |
| EXPECT_OBJ_PTR_EQ(table.LookupByDescriptor(h_X.Get()), h_X.Get()); | |
| EXPECT_OBJ_PTR_EQ(table.Lookup(descriptor_x, ComputeModifiedUtf8Hash(descriptor_x)), h_X.Get()); | |
| EXPECT_TRUE(table.Lookup("NOT_THERE", ComputeModifiedUtf8Hash("NOT_THERE")) == nullptr); | |
| EXPECT_EQ(table.NumZygoteClasses(class_loader.Get()), 0u); | |
| EXPECT_EQ(table.NumNonZygoteClasses(class_loader.Get()), 1u); | |
| // Create the zygote snapshot and ensure the accounting is correct. | |
| table.FreezeSnapshot(); | |
| EXPECT_EQ(table.NumZygoteClasses(class_loader.Get()), 1u); | |
| EXPECT_EQ(table.NumNonZygoteClasses(class_loader.Get()), 0u); | |
| // Test inserting and related lookup functions. | |
| EXPECT_TRUE(table.LookupByDescriptor(h_Y.Get()) == nullptr); | |
| table.Insert(h_Y.Get()); | |
| EXPECT_OBJ_PTR_EQ(table.LookupByDescriptor(h_X.Get()), h_X.Get()); | |
| EXPECT_OBJ_PTR_EQ(table.LookupByDescriptor(h_Y.Get()), h_Y.Get()); | |
| EXPECT_EQ(table.NumZygoteClasses(class_loader.Get()), 1u); | |
| EXPECT_EQ(table.NumNonZygoteClasses(class_loader.Get()), 1u); | |
| // Test adding / clearing strong roots. | |
| EXPECT_TRUE(table.InsertStrongRoot(obj_X.Get())); | |
| EXPECT_FALSE(table.InsertStrongRoot(obj_X.Get())); | |
| table.ClearStrongRoots(); | |
| EXPECT_TRUE(table.InsertStrongRoot(obj_X.Get())); | |
| // Collect all the roots and make sure there is nothing missing. | |
| CollectRootVisitor roots; | |
| table.VisitRoots(roots); | |
| EXPECT_TRUE(roots.roots_.find(h_X.Get()) != roots.roots_.end()); | |
| EXPECT_TRUE(roots.roots_.find(h_Y.Get()) != roots.roots_.end()); | |
| EXPECT_TRUE(roots.roots_.find(obj_X.Get()) != roots.roots_.end()); | |
| // Checks that vising only classes works. | |
| std::set<mirror::Class*> classes; | |
| table.Visit([&classes](ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) { | |
| classes.insert(klass.Ptr()); | |
| return true; | |
| }); | |
| EXPECT_TRUE(classes.find(h_X.Get()) != classes.end()); | |
| EXPECT_TRUE(classes.find(h_Y.Get()) != classes.end()); | |
| EXPECT_EQ(classes.size(), 2u); | |
| classes.clear(); | |
| table.Visit([&classes](ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) { | |
| classes.insert(klass.Ptr()); | |
| // Return false to exit the Visit early. | |
| return false; | |
| }); | |
| EXPECT_EQ(classes.size(), 1u); | |
| // Test that reading a class set from memory works. | |
| ClassTable::ClassSet temp_set; | |
| table.Visit([&temp_set](ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) { | |
| temp_set.insert(ClassTable::TableSlot(klass)); | |
| return true; | |
| }); | |
| const size_t count = temp_set.WriteToMemory(nullptr); | |
| std::unique_ptr<uint8_t[]> buffer(new uint8_t[count]()); | |
| ASSERT_EQ(temp_set.WriteToMemory(&buffer[0]), count); | |
| ClassTable table2; | |
| size_t count2 = table2.ReadFromMemory(&buffer[0]); | |
| EXPECT_EQ(count, count2); | |
| // Strong roots are not serialized, only classes. | |
| EXPECT_OBJ_PTR_EQ(table2.LookupByDescriptor(h_X.Get()), h_X.Get()); | |
| EXPECT_OBJ_PTR_EQ(table2.LookupByDescriptor(h_Y.Get()), h_Y.Get()); | |
| // TODO: Add tests for UpdateClass, InsertOatFile. | |
| } | |
| } // namespace mirror | |
| } // namespace art |