diff options
author | Mathieu Chartier <mathieuc@google.com> | 2014-02-13 18:02:13 -0800 |
---|---|---|
committer | Mathieu Chartier <mathieuc@google.com> | 2014-02-18 10:45:12 -0800 |
commit | 815873ecc312b1d231acce71e1a16f42cdaf09f2 (patch) | |
tree | 18ba2fa951775e60b240271bfe975e6e2cfc654c | |
parent | 2befd09cf4fe89a18a655f3e1dd310831bfa769f (diff) | |
download | art-815873ecc312b1d231acce71e1a16f42cdaf09f2.zip art-815873ecc312b1d231acce71e1a16f42cdaf09f2.tar.gz art-815873ecc312b1d231acce71e1a16f42cdaf09f2.tar.bz2 |
Change root visitor to use Object**.
Simplifies code and improves the performance of root visiting since
we usually don't need to check to see if the object moved.
Change-Id: Iba998f5a15ae1fa1b53ca5226dd2168a411196cf
38 files changed, 181 insertions, 148 deletions
diff --git a/runtime/arch/arm/context_arm.h b/runtime/arch/arm/context_arm.h index 020cae0..4a0d082 100644 --- a/runtime/arch/arm/context_arm.h +++ b/runtime/arch/arm/context_arm.h @@ -45,6 +45,11 @@ class ArmContext : public Context { SetGPR(PC, new_pc); } + virtual uintptr_t* GetGPRAddress(uint32_t reg) { + DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters)); + return gprs_[reg]; + } + virtual uintptr_t GetGPR(uint32_t reg) { DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters)); return *gprs_[reg]; diff --git a/runtime/arch/context.h b/runtime/arch/context.h index 3d11178..83bbb11 100644 --- a/runtime/arch/context.h +++ b/runtime/arch/context.h @@ -49,6 +49,9 @@ class Context { // Set the program counter value virtual void SetPC(uintptr_t new_pc) = 0; + // Gets the given GPRs address. + virtual uintptr_t* GetGPRAddress(uint32_t reg) = 0; + // Read the given GPR virtual uintptr_t GetGPR(uint32_t reg) = 0; diff --git a/runtime/arch/mips/context_mips.h b/runtime/arch/mips/context_mips.h index 4145cd3..d5f27ae 100644 --- a/runtime/arch/mips/context_mips.h +++ b/runtime/arch/mips/context_mips.h @@ -43,6 +43,11 @@ class MipsContext : public Context { SetGPR(RA, new_pc); } + virtual uintptr_t* GetGPRAddress(uint32_t reg) { + DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters)); + return gprs_[reg]; + } + virtual uintptr_t GetGPR(uint32_t reg) { CHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters)); return *gprs_[reg]; diff --git a/runtime/arch/x86/context_x86.cc b/runtime/arch/x86/context_x86.cc index d7dca64..5cf3001 100644 --- a/runtime/arch/x86/context_x86.cc +++ b/runtime/arch/x86/context_x86.cc @@ -26,7 +26,7 @@ namespace x86 { static const uintptr_t gZero = 0; void X86Context::Reset() { - for (int i = 0; i < kNumberOfCpuRegisters; i++) { + for (size_t i = 0; i < kNumberOfCpuRegisters; i++) { gprs_[i] = NULL; } gprs_[ESP] = &esp_; diff --git a/runtime/arch/x86/context_x86.h b/runtime/arch/x86/context_x86.h index 598314d..031fc92 100644 --- a/runtime/arch/x86/context_x86.h +++ b/runtime/arch/x86/context_x86.h @@ -43,8 +43,12 @@ class X86Context : public Context { eip_ = new_pc; } + virtual uintptr_t* GetGPRAddress(uint32_t reg) { + DCHECK_LT(reg, kNumberOfCpuRegisters); + return gprs_[reg]; + } + virtual uintptr_t GetGPR(uint32_t reg) { - const uint32_t kNumberOfCpuRegisters = 8; DCHECK_LT(reg, kNumberOfCpuRegisters); return *gprs_[reg]; } diff --git a/runtime/arch/x86_64/context_x86_64.cc b/runtime/arch/x86_64/context_x86_64.cc index 4d1131c..1310402 100644 --- a/runtime/arch/x86_64/context_x86_64.cc +++ b/runtime/arch/x86_64/context_x86_64.cc @@ -26,7 +26,7 @@ namespace x86_64 { static const uintptr_t gZero = 0; void X86_64Context::Reset() { - for (int i = 0; i < kNumberOfCpuRegisters; i++) { + for (size_t i = 0; i < kNumberOfCpuRegisters; i++) { gprs_[i] = NULL; } gprs_[RSP] = &rsp_; diff --git a/runtime/arch/x86_64/context_x86_64.h b/runtime/arch/x86_64/context_x86_64.h index 3e49165..37cd74c 100644 --- a/runtime/arch/x86_64/context_x86_64.h +++ b/runtime/arch/x86_64/context_x86_64.h @@ -43,8 +43,12 @@ class X86_64Context : public Context { rip_ = new_pc; } + virtual uintptr_t* GetGPRAddress(uint32_t reg) { + DCHECK_LT(reg, kNumberOfCpuRegisters); + return gprs_[reg]; + } + virtual uintptr_t GetGPR(uint32_t reg) { - const uint32_t kNumberOfCpuRegisters = 8; DCHECK_LT(reg, kNumberOfCpuRegisters); return *gprs_[reg]; } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 3863ee5..f8a20d0 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1120,14 +1120,13 @@ void ClassLinker::InitFromImage() { // reinit references to when reinitializing a ClassLinker from a // mapped image. void ClassLinker::VisitRoots(RootCallback* callback, void* arg, bool only_dirty, bool clean_dirty) { - class_roots_ = down_cast<mirror::ObjectArray<mirror::Class>*>( - callback(class_roots_, arg, 0, kRootVMInternal)); + callback(reinterpret_cast<mirror::Object**>(&class_roots_), arg, 0, kRootVMInternal); Thread* self = Thread::Current(); { ReaderMutexLock mu(self, dex_lock_); if (!only_dirty || dex_caches_dirty_) { for (mirror::DexCache*& dex_cache : dex_caches_) { - dex_cache = down_cast<mirror::DexCache*>(callback(dex_cache, arg, 0, kRootVMInternal)); + callback(reinterpret_cast<mirror::Object**>(&dex_cache), arg, 0, kRootVMInternal); DCHECK(dex_cache != nullptr); } if (clean_dirty) { @@ -1135,25 +1134,21 @@ void ClassLinker::VisitRoots(RootCallback* callback, void* arg, bool only_dirty, } } } - { WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); if (!only_dirty || class_table_dirty_) { for (std::pair<const size_t, mirror::Class*>& it : class_table_) { - it.second = down_cast<mirror::Class*>(callback(it.second, arg, 0, kRootStickyClass)); + callback(reinterpret_cast<mirror::Object**>(&it.second), arg, 0, kRootStickyClass); DCHECK(it.second != nullptr); } if (clean_dirty) { class_table_dirty_ = false; } } - // We deliberately ignore the class roots in the image since we // handle image roots by using the MS/CMS rescanning of dirty cards. } - - array_iftable_ = reinterpret_cast<mirror::IfTable*>(callback(array_iftable_, arg, 0, - kRootVMInternal)); + callback(reinterpret_cast<mirror::Object**>(&array_iftable_), arg, 0, kRootVMInternal); DCHECK(array_iftable_ != nullptr); } diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 28ed6c4..ebf02fe 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -339,9 +339,8 @@ class ClassLinkerTest : public CommonTest { } } - static mirror::Object* TestRootVisitor(mirror::Object* root, void*, uint32_t, RootType) { - EXPECT_TRUE(root != NULL); - return root; + static void TestRootVisitor(mirror::Object** root, void*, uint32_t, RootType) { + EXPECT_TRUE(*root != NULL); } }; diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc index 6258070..06127c1 100644 --- a/runtime/gc/accounting/mod_union_table.cc +++ b/runtime/gc/accounting/mod_union_table.cc @@ -70,7 +70,7 @@ class ModUnionClearCardVisitor { class ModUnionUpdateObjectReferencesVisitor { public: - ModUnionUpdateObjectReferencesVisitor(RootCallback* callback, void* arg) + ModUnionUpdateObjectReferencesVisitor(MarkObjectCallback* callback, void* arg) : callback_(callback), arg_(arg) { } @@ -80,7 +80,7 @@ class ModUnionUpdateObjectReferencesVisitor { bool /* is_static */) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { // Only add the reference if it is non null and fits our criteria. if (ref != nullptr) { - Object* new_ref = callback_(ref, arg_, 0, kRootVMInternal); + Object* new_ref = callback_(ref, arg_); if (new_ref != ref) { // Use SetFieldObjectWithoutWriteBarrier to avoid card mark as an optimization which // reduces dirtied pages and improves performance. @@ -94,13 +94,13 @@ class ModUnionUpdateObjectReferencesVisitor { } private: - RootCallback* const callback_; + MarkObjectCallback* const callback_; void* arg_; }; class ModUnionScanImageRootVisitor { public: - ModUnionScanImageRootVisitor(RootCallback* callback, void* arg) + ModUnionScanImageRootVisitor(MarkObjectCallback* callback, void* arg) : callback_(callback), arg_(arg) {} void operator()(Object* root) const @@ -112,7 +112,7 @@ class ModUnionScanImageRootVisitor { } private: - RootCallback* const callback_; + MarkObjectCallback* const callback_; void* const arg_; }; @@ -265,7 +265,8 @@ void ModUnionTableReferenceCache::Dump(std::ostream& os) { } } -void ModUnionTableReferenceCache::UpdateAndMarkReferences(RootCallback* callback, void* arg) { +void ModUnionTableReferenceCache::UpdateAndMarkReferences(MarkObjectCallback* callback, + void* arg) { Heap* heap = GetHeap(); CardTable* card_table = heap->GetCardTable(); @@ -300,7 +301,7 @@ void ModUnionTableReferenceCache::UpdateAndMarkReferences(RootCallback* callback for (mirror::HeapReference<Object>* obj_ptr : ref.second) { Object* obj = obj_ptr->AsMirrorPtr(); if (obj != nullptr) { - Object* new_obj = callback(obj, arg, 0, kRootVMInternal); + Object* new_obj = callback(obj, arg); // Avoid dirtying pages in the image unless necessary. if (new_obj != obj) { obj_ptr->Assign(new_obj); @@ -322,7 +323,7 @@ void ModUnionTableCardCache::ClearCards() { } // Mark all references to the alloc space(s). -void ModUnionTableCardCache::UpdateAndMarkReferences(RootCallback* callback, void* arg) { +void ModUnionTableCardCache::UpdateAndMarkReferences(MarkObjectCallback* callback, void* arg) { CardTable* card_table = heap_->GetCardTable(); ModUnionScanImageRootVisitor scan_visitor(callback, arg); SpaceBitmap* bitmap = space_->GetLiveBitmap(); diff --git a/runtime/gc/accounting/mod_union_table.h b/runtime/gc/accounting/mod_union_table.h index 7d5d8d2..2e22a11 100644 --- a/runtime/gc/accounting/mod_union_table.h +++ b/runtime/gc/accounting/mod_union_table.h @@ -69,7 +69,7 @@ class ModUnionTable { // Update the mod-union table using data stored by ClearCards. There may be multiple ClearCards // before a call to update, for example, back-to-back sticky GCs. Also mark references to other // spaces which are stored in the mod-union table. - virtual void UpdateAndMarkReferences(RootCallback* callback, void* arg) = 0; + virtual void UpdateAndMarkReferences(MarkObjectCallback* callback, void* arg) = 0; // Verification, sanity checks that we don't have clean cards which conflict with out cached data // for said cards. Exclusive lock is required since verify sometimes uses @@ -106,7 +106,7 @@ class ModUnionTableReferenceCache : public ModUnionTable { void ClearCards(); // Update table based on cleared cards and mark all references to the other spaces. - void UpdateAndMarkReferences(RootCallback* callback, void* arg) + void UpdateAndMarkReferences(MarkObjectCallback* callback, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); @@ -142,7 +142,7 @@ class ModUnionTableCardCache : public ModUnionTable { void ClearCards(); // Mark all references to the alloc space(s). - void UpdateAndMarkReferences(RootCallback* callback, void* arg) + void UpdateAndMarkReferences(MarkObjectCallback* callback, void* arg) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index dbbc115..006c271 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -273,7 +273,7 @@ void MarkSweep::UpdateAndMarkModUnion() { TimingLogger::ScopedSplit split(name, &timings_); accounting::ModUnionTable* mod_union_table = heap_->FindModUnionTableFromSpace(space); CHECK(mod_union_table != nullptr); - mod_union_table->UpdateAndMarkReferences(MarkRootCallback, this); + mod_union_table->UpdateAndMarkReferences(MarkObjectCallback, this); } } } @@ -532,20 +532,25 @@ void MarkSweep::MarkRoot(const Object* obj) { } } -mirror::Object* MarkSweep::MarkRootParallelCallback(mirror::Object* root, void* arg, - uint32_t /*thread_id*/, RootType /*root_type*/) { +void MarkSweep::MarkRootParallelCallback(mirror::Object** root, void* arg, uint32_t /*thread_id*/, + RootType /*root_type*/) { DCHECK(root != NULL); DCHECK(arg != NULL); - reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNullParallel(root); - return root; + reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNullParallel(*root); } -Object* MarkSweep::MarkRootCallback(Object* root, void* arg, uint32_t /*thread_id*/, - RootType /*root_type*/) { +void MarkSweep::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id*/, + RootType /*root_type*/) { DCHECK(root != nullptr); DCHECK(arg != nullptr); - reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNull(root); - return root; + reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNull(*root); +} + +mirror::Object* MarkSweep::MarkObjectCallback(mirror::Object* object, void* arg) { + DCHECK(object != nullptr); + DCHECK(arg != nullptr); + reinterpret_cast<MarkSweep*>(arg)->MarkObjectNonNull(object); + return object; } void MarkSweep::VerifyRootCallback(const Object* root, void* arg, size_t vreg, diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h index 8bc0bb5..6a48cf7 100644 --- a/runtime/gc/collector/mark_sweep.h +++ b/runtime/gc/collector/mark_sweep.h @@ -180,13 +180,18 @@ class MarkSweep : public GarbageCollector { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - static mirror::Object* MarkRootCallback(mirror::Object* root, void* arg, uint32_t thread_id, - RootType root_type) + static void MarkRootCallback(mirror::Object** root, void* arg, uint32_t thread_id, + RootType root_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); - static mirror::Object* MarkRootParallelCallback(mirror::Object* root, void* arg, - uint32_t thread_id, RootType root_type); + static mirror::Object* MarkObjectCallback(mirror::Object* object, void* arg) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_); + + static void MarkRootParallelCallback(mirror::Object** root, void* arg, uint32_t thread_id, + RootType root_type) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Marks an object. void MarkObject(const mirror::Object* obj) diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index 6d9496e..d64ec61 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -239,7 +239,7 @@ void SemiSpace::UpdateAndMarkModUnion() { space->IsZygoteSpace() ? "UpdateAndMarkZygoteModUnionTable" : "UpdateAndMarkImageModUnionTable", &timings_); - table->UpdateAndMarkReferences(MarkRootCallback, this); + table->UpdateAndMarkReferences(MarkObjectCallback, this); } else { // If a bump pointer space only collection, the non-moving // space is added to the immune space. But the non-moving @@ -580,11 +580,17 @@ mirror::Object* SemiSpace::RecursiveMarkObjectCallback(mirror::Object* root, voi return ret; } -Object* SemiSpace::MarkRootCallback(Object* root, void* arg, uint32_t /*thread_id*/, - RootType /*root_type*/) { +void SemiSpace::MarkRootCallback(Object** root, void* arg, uint32_t /*thread_id*/, + RootType /*root_type*/) { DCHECK(root != nullptr); DCHECK(arg != nullptr); - return reinterpret_cast<SemiSpace*>(arg)->MarkObject(root); + *root = reinterpret_cast<SemiSpace*>(arg)->MarkObject(*root); +} + +Object* SemiSpace::MarkObjectCallback(Object* object, void* arg) { + DCHECK(object != nullptr); + DCHECK(arg != nullptr); + return reinterpret_cast<SemiSpace*>(arg)->MarkObject(object); } // Marks all objects in the root set. diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h index 89e2002..89fe326 100644 --- a/runtime/gc/collector/semi_space.h +++ b/runtime/gc/collector/semi_space.h @@ -142,10 +142,13 @@ class SemiSpace : public GarbageCollector { static void VisitObjectReferencesAndClass(mirror::Object* obj, const Visitor& visitor) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - static mirror::Object* MarkRootCallback(mirror::Object* root, void* arg, uint32_t /*tid*/, - RootType /*root_type*/) + static void MarkRootCallback(mirror::Object** root, void* arg, uint32_t /*tid*/, + RootType /*root_type*/) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + static mirror::Object* MarkObjectCallback(mirror::Object* objecgt, void* arg) + EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); + static mirror::Object* RecursiveMarkObjectCallback(mirror::Object* root, void* arg) EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 7613a31..061828c 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -1703,13 +1703,12 @@ void Heap::FinishGC(Thread* self, collector::GcType gc_type) { gc_complete_cond_->Broadcast(self); } -static mirror::Object* RootMatchesObjectVisitor(mirror::Object* root, void* arg, - uint32_t /*thread_id*/, RootType /*root_type*/) { +static void RootMatchesObjectVisitor(mirror::Object** root, void* arg, uint32_t /*thread_id*/, + RootType /*root_type*/) { mirror::Object* obj = reinterpret_cast<mirror::Object*>(arg); - if (root == obj) { + if (*root == obj) { LOG(INFO) << "Object " << obj << " is a root"; } - return root; } class ScanVisitor { @@ -1831,11 +1830,10 @@ class VerifyReferenceVisitor { return heap_->IsLiveObjectLocked(obj, true, false, true); } - static mirror::Object* VerifyRoots(mirror::Object* root, void* arg, uint32_t /*thread_id*/, - RootType /*root_type*/) { + static void VerifyRoots(mirror::Object** root, void* arg, uint32_t /*thread_id*/, + RootType /*root_type*/) { VerifyReferenceVisitor* visitor = reinterpret_cast<VerifyReferenceVisitor*>(arg); - (*visitor)(nullptr, root, MemberOffset(0), true); - return root; + (*visitor)(nullptr, *root, MemberOffset(0), true); } private: @@ -2072,7 +2070,7 @@ void Heap::ProcessCards(TimingLogger& timings) { } } -static mirror::Object* IdentityRootCallback(mirror::Object* obj, void*, uint32_t, RootType) { +static mirror::Object* IdentityMarkObjectCallback(mirror::Object* obj, void*) { return obj; } @@ -2111,7 +2109,7 @@ void Heap::PreGcVerification(collector::GarbageCollector* gc) { ReaderMutexLock reader_lock(self, *Locks::heap_bitmap_lock_); for (const auto& table_pair : mod_union_tables_) { accounting::ModUnionTable* mod_union_table = table_pair.second; - mod_union_table->UpdateAndMarkReferences(IdentityRootCallback, nullptr); + mod_union_table->UpdateAndMarkReferences(IdentityMarkObjectCallback, nullptr); mod_union_table->Verify(); } thread_list->ResumeAll(); diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc index ae03dd9..c5a8328 100644 --- a/runtime/hprof/hprof.cc +++ b/runtime/hprof/hprof.cc @@ -496,12 +496,12 @@ class Hprof { } private: - static mirror::Object* RootVisitor(mirror::Object* obj, void* arg, uint32_t thread_id, - RootType root_type) + static void RootVisitor(mirror::Object** obj, void* arg, uint32_t thread_id, RootType root_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK(arg != NULL); - reinterpret_cast<Hprof*>(arg)->VisitRoot(obj, thread_id, root_type); - return obj; + DCHECK(arg != nullptr); + DCHECK(obj != nullptr); + DCHECK(*obj != nullptr); + reinterpret_cast<Hprof*>(arg)->VisitRoot(*obj, thread_id, root_type); } static void VisitObjectCallback(mirror::Object* obj, void* arg) diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc index 4a02d74..54c7b6e 100644 --- a/runtime/indirect_reference_table.cc +++ b/runtime/indirect_reference_table.cc @@ -312,7 +312,7 @@ bool IndirectReferenceTable::Remove(uint32_t cookie, IndirectRef iref) { void IndirectReferenceTable::VisitRoots(RootCallback* callback, void* arg, uint32_t tid, RootType root_type) { for (auto ref : *this) { - *ref = callback(const_cast<mirror::Object*>(*ref), arg, tid, root_type); + callback(ref, arg, tid, root_type); DCHECK(*ref != nullptr); } } diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc index cc49d67..660efe4 100644 --- a/runtime/intern_table.cc +++ b/runtime/intern_table.cc @@ -48,9 +48,8 @@ void InternTable::VisitRoots(RootCallback* callback, void* arg, MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); if (!only_dirty || is_dirty_) { for (auto& strong_intern : strong_interns_) { - strong_intern.second = - down_cast<mirror::String*>(callback(strong_intern.second, arg, 0, - kRootInternedString)); + callback(reinterpret_cast<mirror::Object**>(&strong_intern.second), arg, 0, + kRootInternedString); DCHECK(strong_intern.second != nullptr); } if (clean_dirty) { diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 876de05..362df8c 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -505,7 +505,7 @@ class SharedLibrary { void VisitRoots(RootCallback* visitor, void* arg) { if (class_loader_ != nullptr) { - class_loader_ = visitor(class_loader_, arg, 0, kRootVMInternal); + visitor(&class_loader_, arg, 0, kRootVMInternal); } } diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h index 35ea2b3..7c5de5e 100644 --- a/runtime/mirror/array-inl.h +++ b/runtime/mirror/array-inl.h @@ -113,7 +113,7 @@ inline Array* Array::Alloc(Thread* self, Class* array_class, int32_t component_c template<class T> inline void PrimitiveArray<T>::VisitRoots(RootCallback* callback, void* arg) { if (array_class_ != nullptr) { - array_class_ = down_cast<Class*>(callback(array_class_, arg, 0, kRootStickyClass)); + callback(reinterpret_cast<mirror::Object**>(&array_class_), arg, 0, kRootStickyClass); } } diff --git a/runtime/mirror/art_field.cc b/runtime/mirror/art_field.cc index dd628ea..7740213 100644 --- a/runtime/mirror/art_field.cc +++ b/runtime/mirror/art_field.cc @@ -55,8 +55,8 @@ void ArtField::SetOffset(MemberOffset num_bytes) { void ArtField::VisitRoots(RootCallback* callback, void* arg) { if (java_lang_reflect_ArtField_ != nullptr) { - java_lang_reflect_ArtField_ = down_cast<mirror::Class*>( - callback(java_lang_reflect_ArtField_, arg, 0, kRootStickyClass)); + callback(reinterpret_cast<mirror::Object**>(&java_lang_reflect_ArtField_), arg, 0, + kRootStickyClass); } } diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index b5c87ad..d5f7597 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -43,8 +43,8 @@ Class* ArtMethod::java_lang_reflect_ArtMethod_ = NULL; void ArtMethod::VisitRoots(RootCallback* callback, void* arg) { if (java_lang_reflect_ArtMethod_ != nullptr) { - java_lang_reflect_ArtMethod_ = down_cast<mirror::Class*>( - callback(java_lang_reflect_ArtMethod_, arg, 0, kRootStickyClass)); + callback(reinterpret_cast<mirror::Object**>(&java_lang_reflect_ArtMethod_), arg, 0, + kRootStickyClass); } } diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 6446d02..3208de9 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -52,8 +52,7 @@ void Class::ResetClass() { void Class::VisitRoots(RootCallback* callback, void* arg) { if (java_lang_Class_ != nullptr) { - java_lang_Class_ = down_cast<Class*>( - callback(java_lang_Class_, arg, 0, kRootStickyClass)); + callback(reinterpret_cast<mirror::Object**>(&java_lang_Class_), arg, 0, kRootStickyClass); } } diff --git a/runtime/mirror/stack_trace_element.cc b/runtime/mirror/stack_trace_element.cc index 6bc695d..02a396a 100644 --- a/runtime/mirror/stack_trace_element.cc +++ b/runtime/mirror/stack_trace_element.cc @@ -70,8 +70,8 @@ void StackTraceElement::Init(SirtRef<String>& declaring_class, SirtRef<String>& void StackTraceElement::VisitRoots(RootCallback* callback, void* arg) { if (java_lang_StackTraceElement_ != nullptr) { - java_lang_StackTraceElement_ = down_cast<Class*>( - callback(java_lang_StackTraceElement_, arg, 0, kRootStickyClass)); + callback(reinterpret_cast<mirror::Object**>(&java_lang_StackTraceElement_), arg, 0, + kRootStickyClass); } } diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc index cd63c39..3f35210 100644 --- a/runtime/mirror/string.cc +++ b/runtime/mirror/string.cc @@ -288,7 +288,7 @@ int32_t String::CompareTo(String* rhs) { void String::VisitRoots(RootCallback* callback, void* arg) { if (java_lang_String_ != nullptr) { - java_lang_String_ = down_cast<Class*>(callback(java_lang_String_, arg, 0, kRootStickyClass)); + callback(reinterpret_cast<mirror::Object**>(&java_lang_String_), arg, 0, kRootStickyClass); } } diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc index fef7d36..4c53993 100644 --- a/runtime/mirror/throwable.cc +++ b/runtime/mirror/throwable.cc @@ -99,8 +99,7 @@ void Throwable::ResetClass() { void Throwable::VisitRoots(RootCallback* callback, void* arg) { if (java_lang_Throwable_ != nullptr) { - java_lang_Throwable_ = down_cast<Class*>(callback(java_lang_Throwable_, arg, 0, - kRootStickyClass)); + callback(reinterpret_cast<mirror::Object**>(&java_lang_Throwable_), arg, 0, kRootStickyClass); } } diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 5267069..def3292 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -181,14 +181,12 @@ static void VMRuntime_concurrentGC(JNIEnv* env, jobject) { typedef std::map<std::string, mirror::String*> StringTable; -static mirror::Object* PreloadDexCachesStringsCallback(mirror::Object* root, void* arg, - uint32_t /*thread_id*/, - RootType /*root_type*/) +static void PreloadDexCachesStringsCallback(mirror::Object** root, void* arg, + uint32_t /*thread_id*/, RootType /*root_type*/) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { StringTable& table = *reinterpret_cast<StringTable*>(arg); - mirror::String* string = const_cast<mirror::Object*>(root)->AsString(); + mirror::String* string = const_cast<mirror::Object*>(*root)->AsString(); table[string->ToModifiedUtf8()] = string; - return root; } // Based on ClassLinker::ResolveString. diff --git a/runtime/object_callbacks.h b/runtime/object_callbacks.h index 8e3c529..6af338b 100644 --- a/runtime/object_callbacks.h +++ b/runtime/object_callbacks.h @@ -46,8 +46,8 @@ enum RootType { // Returns the new address of the object, returns root if it has not moved. tid and root_type are // only used by hprof. -typedef mirror::Object* (RootCallback)(mirror::Object* root, void* arg, uint32_t thread_id, - RootType root_type) __attribute__((warn_unused_result)); +typedef void (RootCallback)(mirror::Object** root, void* arg, uint32_t thread_id, + RootType root_type); // A callback for visiting an object in the heap. typedef void (ObjectCallback)(mirror::Object* obj, void* arg); // A callback used for marking an object, returns the new address of the object if the object moved. diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc index a9b17e0..f43a15b 100644 --- a/runtime/reference_table.cc +++ b/runtime/reference_table.cc @@ -234,7 +234,7 @@ void ReferenceTable::Dump(std::ostream& os, const Table& entries) { void ReferenceTable::VisitRoots(RootCallback* visitor, void* arg, uint32_t tid, RootType root_type) { for (auto& ref : entries_) { - ref = visitor(ref, arg, tid, root_type); + visitor(&ref, arg, tid, root_type); } } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index e1b0ed4..e66e5af 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1333,26 +1333,23 @@ void Runtime::VisitNonThreadRoots(RootCallback* callback, void* arg) { mirror::PrimitiveArray<int16_t>::VisitRoots(callback, arg); // ShortArray java_vm_->VisitRoots(callback, arg); if (pre_allocated_OutOfMemoryError_ != nullptr) { - pre_allocated_OutOfMemoryError_ = down_cast<mirror::Throwable*>( - callback(pre_allocated_OutOfMemoryError_, arg, 0, kRootVMInternal)); + callback(reinterpret_cast<mirror::Object**>(&pre_allocated_OutOfMemoryError_), arg, 0, + kRootVMInternal); DCHECK(pre_allocated_OutOfMemoryError_ != nullptr); } - resolution_method_ = down_cast<mirror::ArtMethod*>(callback(resolution_method_, arg, 0, - kRootVMInternal)); + callback(reinterpret_cast<mirror::Object**>(&resolution_method_), arg, 0, kRootVMInternal); DCHECK(resolution_method_ != nullptr); if (HasImtConflictMethod()) { - imt_conflict_method_ = down_cast<mirror::ArtMethod*>(callback(imt_conflict_method_, arg, 0, - kRootVMInternal)); + callback(reinterpret_cast<mirror::Object**>(&imt_conflict_method_), arg, 0, kRootVMInternal); } if (HasDefaultImt()) { - default_imt_ = down_cast<mirror::ObjectArray<mirror::ArtMethod>*>(callback(default_imt_, arg, - 0, kRootVMInternal)); + callback(reinterpret_cast<mirror::Object**>(&default_imt_), arg, 0, kRootVMInternal); } for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) { if (callee_save_methods_[i] != nullptr) { - callee_save_methods_[i] = down_cast<mirror::ArtMethod*>( - callback(callee_save_methods_[i], arg, 0, kRootVMInternal)); + callback(reinterpret_cast<mirror::Object**>(&callee_save_methods_[i]), arg, 0, + kRootVMInternal); } } { diff --git a/runtime/stack.cc b/runtime/stack.cc index fd7d981..864b86a 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -189,6 +189,11 @@ void StackVisitor::SetVReg(mirror::ArtMethod* m, uint16_t vreg, uint32_t new_val } } +uintptr_t* StackVisitor::GetGPRAddress(uint32_t reg) const { + DCHECK(cur_quick_frame_ != NULL) << "This is a quick frame routine"; + return context_->GetGPRAddress(reg); +} + uintptr_t StackVisitor::GetGPR(uint32_t reg) const { DCHECK(cur_quick_frame_ != NULL) << "This is a quick frame routine"; return context_->GetGPR(reg); diff --git a/runtime/stack.h b/runtime/stack.h index 2d56a74..7e9889e 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -565,6 +565,7 @@ class StackVisitor { void SetVReg(mirror::ArtMethod* m, uint16_t vreg, uint32_t new_value, VRegKind kind) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + uintptr_t* GetGPRAddress(uint32_t reg) const; uintptr_t GetGPR(uint32_t reg) const; void SetGPR(uint32_t reg, uintptr_t value); diff --git a/runtime/thread.cc b/runtime/thread.cc index 5728391..16655fb 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1004,19 +1004,18 @@ void Thread::AssertNoPendingException() const { } } -static mirror::Object* MonitorExitVisitor(mirror::Object* object, void* arg, uint32_t /*thread_id*/, - RootType /*root_type*/) +static void MonitorExitVisitor(mirror::Object** object, void* arg, uint32_t /*thread_id*/, + RootType /*root_type*/) NO_THREAD_SAFETY_ANALYSIS { Thread* self = reinterpret_cast<Thread*>(arg); - mirror::Object* entered_monitor = object; + mirror::Object* entered_monitor = *object; if (self->HoldsLock(entered_monitor)) { LOG(WARNING) << "Calling MonitorExit on object " - << object << " (" << PrettyTypeOf(object) << ")" + << object << " (" << PrettyTypeOf(entered_monitor) << ")" << " left locked by native thread " << *Thread::Current() << " which is detaching"; entered_monitor->MonitorExit(self); } - return object; } void Thread::Destroy() { @@ -1167,10 +1166,10 @@ void Thread::SirtVisitRoots(RootCallback* visitor, void* arg, uint32_t thread_id for (size_t j = 0; j < num_refs; ++j) { mirror::Object* object = cur->GetReference(j); if (object != nullptr) { - mirror::Object* new_obj = visitor(object, arg, thread_id, kRootNativeStack); - DCHECK(new_obj != nullptr); - if (new_obj != object) { - cur->SetReference(j, new_obj); + mirror::Object* old_obj = object; + visitor(&object, arg, thread_id, kRootNativeStack); + if (old_obj != object) { + cur->SetReference(j, object); } } } @@ -1888,7 +1887,8 @@ class ReferenceMapVisitor : public StackVisitor { for (size_t reg = 0; reg < num_regs; ++reg) { mirror::Object* ref = shadow_frame->GetVRegReference(reg); if (ref != nullptr) { - mirror::Object* new_ref = visitor_(ref, reg, this); + mirror::Object* new_ref = ref; + visitor_(&new_ref, reg, this); if (new_ref != ref) { shadow_frame->SetVRegReference(reg, new_ref); } @@ -1908,7 +1908,8 @@ class ReferenceMapVisitor : public StackVisitor { if (TestBitmap(reg, reg_bitmap)) { mirror::Object* ref = shadow_frame->GetVRegReference(reg); if (ref != nullptr) { - mirror::Object* new_ref = visitor_(ref, reg, this); + mirror::Object* new_ref = ref; + visitor_(&new_ref, reg, this); if (new_ref != ref) { shadow_frame->SetVRegReference(reg, new_ref); } @@ -1944,21 +1945,22 @@ class ReferenceMapVisitor : public StackVisitor { uint32_t vmap_offset; if (vmap_table.IsInContext(reg, kReferenceVReg, &vmap_offset)) { int vmap_reg = vmap_table.ComputeRegister(core_spills, vmap_offset, kReferenceVReg); - mirror::Object* ref = reinterpret_cast<mirror::Object*>(GetGPR(vmap_reg)); - if (ref != nullptr) { - mirror::Object* new_ref = visitor_(ref, reg, this); - if (ref != new_ref) { - SetGPR(vmap_reg, reinterpret_cast<uintptr_t>(new_ref)); - } + // This is sound as spilled GPRs will be word sized (ie 32 or 64bit). + mirror::Object** ref_addr = reinterpret_cast<mirror::Object**>(GetGPRAddress(vmap_reg)); + if (*ref_addr != nullptr) { + visitor_(ref_addr, reg, this); } } else { - uintptr_t* reg_addr = reinterpret_cast<uintptr_t*>( - GetVRegAddr(cur_quick_frame, code_item, core_spills, fp_spills, frame_size, reg)); - mirror::Object* ref = reinterpret_cast<mirror::Object*>(*reg_addr); + StackReference<mirror::Object>* ref_addr = + reinterpret_cast<StackReference<mirror::Object>*>( + GetVRegAddr(cur_quick_frame, code_item, core_spills, fp_spills, frame_size, + reg)); + mirror::Object* ref = ref_addr->AsMirrorPtr(); if (ref != nullptr) { - mirror::Object* new_ref = visitor_(ref, reg, this); + mirror::Object* new_ref = ref; + visitor_(&new_ref, reg, this); if (ref != new_ref) { - *reg_addr = reinterpret_cast<uintptr_t>(new_ref); + ref_addr->Assign(new_ref); } } } @@ -1971,8 +1973,8 @@ class ReferenceMapVisitor : public StackVisitor { } private: - static bool TestBitmap(int reg, const uint8_t* reg_vector) { - return ((reg_vector[reg / 8] >> (reg % 8)) & 0x01) != 0; + static bool TestBitmap(size_t reg, const uint8_t* reg_vector) { + return ((reg_vector[reg / kBitsPerByte] >> (reg % kBitsPerByte)) & 0x01) != 0; } // Visitor for when we visit a root. @@ -1987,8 +1989,8 @@ class RootCallbackVisitor { RootCallbackVisitor(RootCallback* callback, void* arg, uint32_t tid) : callback_(callback), arg_(arg), tid_(tid) {} - mirror::Object* operator()(mirror::Object* obj, size_t, const StackVisitor*) const { - return callback_(obj, arg_, tid_, kRootJavaFrame); + void operator()(mirror::Object** obj, size_t, const StackVisitor*) const { + callback_(obj, arg_, tid_, kRootJavaFrame); } private: @@ -2007,17 +2009,15 @@ void Thread::SetClassLoaderOverride(mirror::ClassLoader* class_loader_override) void Thread::VisitRoots(RootCallback* visitor, void* arg) { uint32_t thread_id = GetThreadId(); if (opeer_ != nullptr) { - opeer_ = visitor(opeer_, arg, thread_id, kRootThreadObject); + visitor(&opeer_, arg, thread_id, kRootThreadObject); } if (exception_ != nullptr) { - exception_ = down_cast<mirror::Throwable*>(visitor(exception_, arg, thread_id, - kRootNativeStack)); + visitor(reinterpret_cast<mirror::Object**>(&exception_), arg, thread_id, kRootNativeStack); } throw_location_.VisitRoots(visitor, arg); if (class_loader_override_ != nullptr) { - class_loader_override_ = - down_cast<mirror::ClassLoader*>(visitor(class_loader_override_, arg, thread_id, - kRootNativeStack)); + visitor(reinterpret_cast<mirror::Object**>(&class_loader_override_), arg, thread_id, + kRootNativeStack); } jni_env_->locals.VisitRoots(visitor, arg, thread_id, kRootJNILocal); jni_env_->monitors.VisitRoots(visitor, arg, thread_id, kRootJNIMonitor); @@ -2030,20 +2030,18 @@ void Thread::VisitRoots(RootCallback* visitor, void* arg) { ReleaseLongJumpContext(context); for (instrumentation::InstrumentationStackFrame& frame : *GetInstrumentationStack()) { if (frame.this_object_ != nullptr) { - frame.this_object_ = visitor(frame.this_object_, arg, thread_id, kRootJavaFrame); + visitor(&frame.this_object_, arg, thread_id, kRootJavaFrame); } DCHECK(frame.method_ != nullptr); - frame.method_ = down_cast<mirror::ArtMethod*>(visitor(frame.method_, arg, thread_id, - kRootJavaFrame)); + visitor(reinterpret_cast<mirror::Object**>(&frame.method_), arg, thread_id, kRootJavaFrame); } } -static mirror::Object* VerifyRoot(mirror::Object* root, void* arg, uint32_t /*thread_id*/, - RootType /*root_type*/) { +static void VerifyRoot(mirror::Object** root, void* arg, uint32_t /*thread_id*/, + RootType /*root_type*/) { DCHECK(root != nullptr); DCHECK(arg != nullptr); - reinterpret_cast<gc::Heap*>(arg)->VerifyObject(root); - return root; + reinterpret_cast<gc::Heap*>(arg)->VerifyObject(*root); } void Thread::VerifyStackImpl() { diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 25f692d..f058dee 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -771,11 +771,10 @@ class VerifyRootWrapperArg { void* const arg_; }; -static mirror::Object* VerifyRootWrapperCallback(mirror::Object* root, void* arg, - uint32_t /*thread_id*/, RootType /*root_type*/) { +static void VerifyRootWrapperCallback(mirror::Object** root, void* arg, uint32_t /*thread_id*/, + RootType /*root_type*/) { VerifyRootWrapperArg* wrapperArg = reinterpret_cast<VerifyRootWrapperArg*>(arg); - wrapperArg->callback_(root, wrapperArg->arg_, 0, NULL); - return root; + wrapperArg->callback_(*root, wrapperArg->arg_, 0, NULL); } void ThreadList::VerifyRoots(VerifyRootCallback* callback, void* arg) const { diff --git a/runtime/throw_location.cc b/runtime/throw_location.cc index 2a1faff..06b6e8d 100644 --- a/runtime/throw_location.cc +++ b/runtime/throw_location.cc @@ -35,11 +35,11 @@ std::string ThrowLocation::Dump() const { void ThrowLocation::VisitRoots(RootCallback* visitor, void* arg) { if (this_object_ != nullptr) { - this_object_ = visitor(this_object_, arg, 0, kRootVMInternal); + visitor(&this_object_, arg, 0, kRootVMInternal); DCHECK(this_object_ != nullptr); } if (method_ != nullptr) { - method_ = down_cast<mirror::ArtMethod*>(visitor(method_, arg, 0, kRootVMInternal)); + visitor(reinterpret_cast<mirror::Object**>(&method_), arg, 0, kRootVMInternal); DCHECK(method_ != nullptr); } } diff --git a/runtime/transaction.cc b/runtime/transaction.cc index 6adcfec..019a322 100644 --- a/runtime/transaction.cc +++ b/runtime/transaction.cc @@ -173,7 +173,8 @@ void Transaction::VisitObjectLogs(RootCallback* callback, void* arg) { for (auto it : object_logs_) { it.second.VisitRoots(callback, arg); mirror::Object* old_root = it.first; - mirror::Object* new_root = callback(old_root, arg, 0, kRootUnknown); + mirror::Object* new_root = old_root; + callback(&new_root, arg, 0, kRootUnknown); if (new_root != old_root) { moving_roots.push_back(std::make_pair(old_root, new_root)); } @@ -201,7 +202,8 @@ void Transaction::VisitArrayLogs(RootCallback* callback, void* arg) { if (old_root->IsObjectArray()) { it.second.VisitRoots(callback, arg); } - mirror::Array* new_root = down_cast<mirror::Array*>(callback(old_root, arg, 0, kRootUnknown)); + mirror::Array* new_root = old_root; + callback(reinterpret_cast<mirror::Object**>(&new_root), arg, 0, kRootUnknown); if (new_root != old_root) { moving_roots.push_back(std::make_pair(old_root, new_root)); } @@ -306,8 +308,10 @@ void Transaction::ObjectLog::VisitRoots(RootCallback* callback, void* arg) { for (auto it : field_values_) { FieldValue& field_value = it.second; if (field_value.kind == ObjectLog::kReference) { - mirror::Object* obj = reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(field_value.value)); - field_value.value = reinterpret_cast<uintptr_t>(callback(obj, arg, 0, kRootUnknown)); + mirror::Object* obj = + reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(field_value.value)); + callback(&obj, arg, 0, kRootUnknown); + field_value.value = reinterpret_cast<uintptr_t>(obj); } } } @@ -350,7 +354,7 @@ void Transaction::InternStringLog::Undo(InternTable* intern_table) { } void Transaction::InternStringLog::VisitRoots(RootCallback* callback, void* arg) { - str_ = down_cast<mirror::String*>(callback(str_, arg, 0, kRootInternedString)); + callback(reinterpret_cast<mirror::Object**>(&str_), arg, 0, kRootInternedString); } void Transaction::ArrayLog::LogValue(size_t index, uint64_t value) { @@ -412,7 +416,8 @@ void Transaction::ArrayLog::UndoArrayWrite(mirror::Array* array, Primitive::Type void Transaction::ArrayLog::VisitRoots(RootCallback* callback, void* arg) { for (auto& it : array_values_) { mirror::Object* obj = reinterpret_cast<mirror::Object*>(static_cast<uintptr_t>(it.second)); - it.second = reinterpret_cast<uintptr_t>(callback(obj, arg, 0, kRootUnknown)); + callback(&obj, arg, 0, kRootUnknown); + it.second = reinterpret_cast<uintptr_t>(obj); } } diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc index e56e670..630ef8a 100644 --- a/runtime/verifier/reg_type.cc +++ b/runtime/verifier/reg_type.cc @@ -971,7 +971,7 @@ void RegType::CheckInvariants() const { void RegType::VisitRoots(RootCallback* callback, void* arg) { if (klass_ != nullptr) { - klass_ = down_cast<mirror::Class*>(callback(klass_, arg, 0, kRootUnknown)); + callback(reinterpret_cast<mirror::Object**>(&klass_), arg, 0, kRootUnknown); } } |