diff options
| -rw-r--r-- | compiler/driver/compiler_driver.cc | 24 | ||||
| -rw-r--r-- | runtime/class_linker.cc | 18 | ||||
| -rw-r--r-- | runtime/class_table-inl.h | 44 | ||||
| -rw-r--r-- | runtime/class_table.cc | 10 | ||||
| -rw-r--r-- | runtime/class_table.h | 11 | ||||
| -rw-r--r-- | runtime/gc/collector/mark_sweep.cc | 2 | ||||
| -rw-r--r-- | runtime/gc/heap.cc | 18 | ||||
| -rw-r--r-- | runtime/gc/heap.h | 6 | ||||
| -rw-r--r-- | runtime/hprof/hprof.cc | 36 | ||||
| -rw-r--r-- | runtime/mirror/class.h | 9 | ||||
| -rw-r--r-- | runtime/mirror/class_loader-inl.h | 43 | ||||
| -rw-r--r-- | runtime/mirror/class_loader.h | 8 | ||||
| -rw-r--r-- | runtime/mirror/object-inl.h | 16 | ||||
| -rw-r--r-- | runtime/mirror/object.h | 6 | ||||
| -rw-r--r-- | runtime/modifiers.h | 3 | ||||
| -rw-r--r-- | runtime/thread_list.cc | 7 | ||||
| -rw-r--r-- | runtime/verifier/method_verifier.cc | 50 | ||||
| -rw-r--r-- | runtime/verifier/method_verifier.h | 4 | ||||
| -rw-r--r-- | runtime/verifier/register_line.cc | 76 | ||||
| -rw-r--r-- | runtime/verifier/register_line.h | 26 | ||||
| -rw-r--r-- | test/800-smali/expected.txt | 1 | ||||
| -rw-r--r-- | test/800-smali/smali/b_20843113.smali | 34 | ||||
| -rw-r--r-- | test/800-smali/src/Main.java | 1 |
23 files changed, 320 insertions, 133 deletions
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 299b99585a..fa4667ec97 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -829,6 +829,18 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { std::set<std::pair<uint16_t, const DexFile*>>& exceptions_to_resolve) : exceptions_to_resolve_(exceptions_to_resolve) {} + virtual bool Visit(mirror::Class* c) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) { + const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); + for (auto& m : c->GetVirtualMethods(pointer_size)) { + ResolveExceptionsForMethod(&m); + } + for (auto& m : c->GetDirectMethods(pointer_size)) { + ResolveExceptionsForMethod(&m); + } + return true; + } + + private: void ResolveExceptionsForMethod(ArtMethod* method_handle) SHARED_REQUIRES(Locks::mutator_lock_) { const DexFile::CodeItem* code_item = method_handle->GetCodeItem(); if (code_item == nullptr) { @@ -864,18 +876,6 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { } } - virtual bool Visit(mirror::Class* c) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) { - const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - for (auto& m : c->GetVirtualMethods(pointer_size)) { - ResolveExceptionsForMethod(&m); - } - for (auto& m : c->GetDirectMethods(pointer_size)) { - ResolveExceptionsForMethod(&m); - } - return true; - } - - private: std::set<std::pair<uint16_t, const DexFile*>>& exceptions_to_resolve_; }; diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 0886e327d9..f19263d757 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -37,6 +37,7 @@ #include "base/unix_file/fd_file.h" #include "base/value_object.h" #include "class_linker-inl.h" +#include "class_table-inl.h" #include "compiler_callbacks.h" #include "debugger.h" #include "dex_file-inl.h" @@ -582,6 +583,7 @@ void ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b // Setup the ClassLoader, verifying the object_size_. class_root = FindSystemClass(self, "Ljava/lang/ClassLoader;"); + class_root->SetClassLoaderClass(); CHECK_EQ(class_root->GetObjectSize(), mirror::ClassLoader::InstanceSize()); SetClassRoot(kJavaLangClassLoader, class_root); @@ -1273,15 +1275,10 @@ void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) { // Moving concurrent: // Need to make sure to not copy ArtMethods without doing read barriers since the roots are // marked concurrently and we don't hold the classlinker_classes_lock_ when we do the copy. - boot_class_table_.VisitRoots(visitor, flags); + boot_class_table_.VisitRoots(buffered_visitor); for (GcRoot<mirror::ClassLoader>& root : class_loaders_) { // May be null for boot ClassLoader. root.VisitRoot(visitor, RootInfo(kRootVMInternal)); - ClassTable* const class_table = root.Read()->GetClassTable(); - if (class_table != nullptr) { - // May be null if we have no classes. - class_table->VisitRoots(visitor, flags); - } } } else if ((flags & kVisitRootFlagNewRoots) != 0) { for (auto& root : new_class_roots_) { @@ -2810,6 +2807,10 @@ mirror::Class* ClassLinker::InsertClass(const char* descriptor, mirror::Class* k } VerifyObject(klass); class_table->InsertWithHash(klass, hash); + if (class_loader != nullptr) { + // This is necessary because we need to have the card dirtied for remembered sets. + Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader); + } if (log_new_class_table_roots_) { new_class_roots_.push_back(GcRoot<mirror::Class>(klass)); } @@ -4375,6 +4376,11 @@ bool ClassLinker::LinkSuperClass(Handle<mirror::Class> klass) { klass->SetFinalizable(); } + // Inherit class loader flag form super class. + if (super->IsClassLoaderClass()) { + klass->SetClassLoaderClass(); + } + // Inherit reference flags (if any) from the superclass. int reference_flags = (super->GetAccessFlags() & kAccReferenceFlagsMask); if (reference_flags != 0) { diff --git a/runtime/class_table-inl.h b/runtime/class_table-inl.h new file mode 100644 index 0000000000..dc60a2c239 --- /dev/null +++ b/runtime/class_table-inl.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ART_RUNTIME_CLASS_TABLE_INL_H_ +#define ART_RUNTIME_CLASS_TABLE_INL_H_ + +#include "class_table.h" + +namespace art { + +template<class Visitor> +void ClassTable::VisitRoots(Visitor& visitor) { + for (ClassSet& class_set : classes_) { + for (GcRoot<mirror::Class>& root : class_set) { + visitor.VisitRoot(root.AddressWithoutBarrier()); + } + } +} + +template<class Visitor> +void ClassTable::VisitRoots(const Visitor& visitor) { + for (ClassSet& class_set : classes_) { + for (GcRoot<mirror::Class>& root : class_set) { + visitor.VisitRoot(root.AddressWithoutBarrier()); + } + } +} + +} // namespace art + +#endif // ART_RUNTIME_CLASS_TABLE_INL_H_ diff --git a/runtime/class_table.cc b/runtime/class_table.cc index c245d4e780..fc8e6c49da 100644 --- a/runtime/class_table.cc +++ b/runtime/class_table.cc @@ -61,16 +61,6 @@ mirror::Class* ClassTable::UpdateClass(const char* descriptor, mirror::Class* kl return existing; } -void ClassTable::VisitRoots(RootVisitor* visitor, VisitRootFlags flags ATTRIBUTE_UNUSED) { - BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor( - visitor, RootInfo(kRootStickyClass)); - for (ClassSet& class_set : classes_) { - for (GcRoot<mirror::Class>& root : class_set) { - buffered_visitor.VisitRoot(root); - } - } -} - bool ClassTable::Visit(ClassVisitor* visitor) { for (ClassSet& class_set : classes_) { for (GcRoot<mirror::Class>& root : class_set) { diff --git a/runtime/class_table.h b/runtime/class_table.h index 418295449f..6b18d9009d 100644 --- a/runtime/class_table.h +++ b/runtime/class_table.h @@ -67,8 +67,15 @@ class ClassTable { mirror::Class* UpdateClass(const char* descriptor, mirror::Class* new_klass, size_t hash) REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_); - void VisitRoots(RootVisitor* visitor, VisitRootFlags flags) - REQUIRES(Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_); + // NO_THREAD_SAFETY_ANALYSIS for object marking requiring heap bitmap lock. + template<class Visitor> + void VisitRoots(Visitor& visitor) + SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_) + NO_THREAD_SAFETY_ANALYSIS; + template<class Visitor> + void VisitRoots(const Visitor& visitor) + SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_) + NO_THREAD_SAFETY_ANALYSIS; // Return false if the callback told us to exit. bool Visit(ClassVisitor* visitor) diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index e2bcca23fb..5799b664e3 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -1276,7 +1276,7 @@ class MarkVisitor { explicit MarkVisitor(MarkSweep* const mark_sweep) ALWAYS_INLINE : mark_sweep_(mark_sweep) { } - void operator()(mirror::Object* obj, MemberOffset offset, bool /* is_static */) const + void operator()(mirror::Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const ALWAYS_INLINE SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) { if (kCheckLocks) { diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index e019d1127d..1033d713a1 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -229,7 +229,8 @@ Heap::Heap(size_t initial_size, alloc_tracking_enabled_(false), backtrace_lock_(nullptr), seen_backtrace_count_(0u), - unique_backtrace_count_(0u) { + unique_backtrace_count_(0u), + gc_disabled_for_shutdown_(false) { if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { LOG(INFO) << "Heap() entering"; } @@ -2401,11 +2402,6 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, bool clear_soft_references) { Thread* self = Thread::Current(); Runtime* runtime = Runtime::Current(); - // Don't allow the GC to start if the runtime is shutting down. This can occur if a Daemon thread - // is still allocating. - if (runtime->IsShuttingDown(self)) { - return collector::kGcTypeNone; - } // If the heap can't run the GC, silently fail and return that no GC was run. switch (gc_type) { case collector::kGcTypePartial: { @@ -2439,6 +2435,9 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, LOG(WARNING) << "Skipping GC due to disable moving GC count " << disable_moving_gc_count_; return collector::kGcTypeNone; } + if (gc_disabled_for_shutdown_) { + return collector::kGcTypeNone; + } collector_type_running_ = collector_type_; } if (gc_cause == kGcCauseForAlloc && runtime->HasStatsEnabled()) { @@ -3852,5 +3851,12 @@ void Heap::CheckGcStressMode(Thread* self, mirror::Object** obj) { } } +void Heap::DisableGCForShutdown() { + Thread* const self = Thread::Current(); + CHECK(Runtime::Current()->IsShuttingDown(self)); + MutexLock mu(self, *gc_complete_lock_); + gc_disabled_for_shutdown_ = true; +} + } // namespace gc } // namespace art diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 4703685f16..283a3cb21c 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -745,6 +745,8 @@ class Heap { SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!Locks::alloc_tracker_lock_); + void DisableGCForShutdown() REQUIRES(!*gc_complete_lock_); + private: class ConcurrentGCTask; class CollectorTransitionTask; @@ -1293,6 +1295,10 @@ class Heap { // Stack trace hashes that we already saw, std::unordered_set<uint64_t> seen_backtraces_ GUARDED_BY(backtrace_lock_); + // We disable GC when we are shutting down the runtime in case there are daemon threads still + // allocating. + bool gc_disabled_for_shutdown_ GUARDED_BY(gc_complete_lock_); + friend class CollectorTransitionTask; friend class collector::GarbageCollector; friend class collector::MarkCompact; diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc index e67ea3fa8f..713797fc3d 100644 --- a/runtime/hprof/hprof.cc +++ b/runtime/hprof/hprof.cc @@ -883,6 +883,7 @@ class Hprof : public SingleRootVisitor { gc::EqAllocRecordTypesPtr<gc::AllocRecordStackTraceElement>> frames_; std::unordered_map<const mirror::Object*, const gc::AllocRecordStackTrace*> allocation_records_; + friend class GcRootVisitor; DISALLOW_COPY_AND_ASSIGN(Hprof); }; @@ -1023,12 +1024,47 @@ void Hprof::MarkRootObject(const mirror::Object* obj, jobject jni_obj, HprofHeap ++objects_in_segment_; } +// Use for visiting the GcRoots held live by ArtFields, ArtMethods, and ClassLoaders. +class GcRootVisitor { + public: + explicit GcRootVisitor(Hprof* hprof) : hprof_(hprof) {} + + void operator()(mirror::Object* obj ATTRIBUTE_UNUSED, + MemberOffset offset ATTRIBUTE_UNUSED, + bool is_static ATTRIBUTE_UNUSED) const {} + + // Note that these don't have read barriers. Its OK however since the GC is guaranteed to not be + // running during the hprof dumping process. + void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const + SHARED_REQUIRES(Locks::mutator_lock_) { + if (!root->IsNull()) { + VisitRoot(root); + } + } + + void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const + SHARED_REQUIRES(Locks::mutator_lock_) { + mirror::Object* obj = root->AsMirrorPtr(); + // The two cases are either classes or dex cache arrays. If it is a dex cache array, then use + // VM internal. Otherwise the object is a declaring class of an ArtField or ArtMethod or a + // class from a ClassLoader. + hprof_->VisitRoot(obj, RootInfo(obj->IsClass() ? kRootStickyClass : kRootVMInternal)); + } + + + private: + Hprof* const hprof_; +}; + void Hprof::DumpHeapObject(mirror::Object* obj) { // Ignore classes that are retired. if (obj->IsClass() && obj->AsClass()->IsRetired()) { return; } + GcRootVisitor visitor(this); + obj->VisitReferences<true>(visitor, VoidFunctor()); + gc::Heap* const heap = Runtime::Current()->GetHeap(); const gc::space::ContinuousSpace* const space = heap->FindContinuousSpaceFromObject(obj, true); HprofHeapId heap_type = HPROF_HEAP_APP; diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 513ab37033..dc60a380e3 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -236,6 +236,15 @@ class MANAGED Class FINAL : public Object { SetAccessFlags(flags | kAccClassIsStringClass); } + ALWAYS_INLINE bool IsClassLoaderClass() SHARED_REQUIRES(Locks::mutator_lock_) { + return (GetField32(AccessFlagsOffset()) & kAccClassIsClassLoaderClass) != 0; + } + + ALWAYS_INLINE void SetClassLoaderClass() SHARED_REQUIRES(Locks::mutator_lock_) { + uint32_t flags = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_)); + SetAccessFlags(flags | kAccClassIsClassLoaderClass); + } + // Returns true if the class is abstract. ALWAYS_INLINE bool IsAbstract() SHARED_REQUIRES(Locks::mutator_lock_) { return (GetAccessFlags() & kAccAbstract) != 0; diff --git a/runtime/mirror/class_loader-inl.h b/runtime/mirror/class_loader-inl.h new file mode 100644 index 0000000000..35f3664fbf --- /dev/null +++ b/runtime/mirror/class_loader-inl.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ART_RUNTIME_MIRROR_CLASS_LOADER_INL_H_ +#define ART_RUNTIME_MIRROR_CLASS_LOADER_INL_H_ + +#include "class_loader.h" + +#include "base/mutex-inl.h" +#include "class_table-inl.h" + +namespace art { +namespace mirror { + +template <const bool kVisitClass, VerifyObjectFlags kVerifyFlags, typename Visitor> +inline void ClassLoader::VisitReferences(mirror::Class* klass, const Visitor& visitor) { + // Visit instance fields first. + VisitInstanceFieldsReferences<kVisitClass>(klass, visitor); + // Visit classes loaded after. + ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); + ClassTable* const class_table = GetClassTable(); + if (class_table != nullptr) { + class_table->VisitRoots(visitor); + } +} + +} // namespace mirror +} // namespace art + +#endif // ART_RUNTIME_MIRROR_CLASS_LOADER_INL_H_ diff --git a/runtime/mirror/class_loader.h b/runtime/mirror/class_loader.h index 940aaa6fb2..21c652a941 100644 --- a/runtime/mirror/class_loader.h +++ b/runtime/mirror/class_loader.h @@ -26,6 +26,8 @@ class ClassTable; namespace mirror { +class Class; + // C++ mirror of java.lang.ClassLoader class MANAGED ClassLoader : public Object { public: @@ -44,6 +46,12 @@ class MANAGED ClassLoader : public Object { SetField64<false>(OFFSET_OF_OBJECT_MEMBER(ClassLoader, class_table_), reinterpret_cast<uint64_t>(class_table)); } + // Visit instance fields of the class loader as well as its associated classes. + // Null class loader is handled by ClassLinker::VisitClassRoots. + template <const bool kVisitClass, VerifyObjectFlags kVerifyFlags, typename Visitor> + void VisitReferences(mirror::Class* klass, const Visitor& visitor) + SHARED_REQUIRES(Locks::mutator_lock_) + REQUIRES(!Locks::classlinker_classes_lock_); private: // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses". diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h index c5610b5a2e..7b1660ba7e 100644 --- a/runtime/mirror/object-inl.h +++ b/runtime/mirror/object-inl.h @@ -25,6 +25,7 @@ #include "array-inl.h" #include "class.h" #include "class_linker.h" +#include "class_loader-inl.h" #include "lock_word-inl.h" #include "monitor.h" #include "object_array-inl.h" @@ -997,6 +998,18 @@ inline void Object::VisitStaticFieldsReferences(mirror::Class* klass, const Visi klass->VisitFieldsReferences<kVisitClass, true>(0, visitor); } + +template<VerifyObjectFlags kVerifyFlags> +inline bool Object::IsClassLoader() { + return GetClass<kVerifyFlags>()->IsClassLoaderClass(); +} + +template<VerifyObjectFlags kVerifyFlags> +inline mirror::ClassLoader* Object::AsClassLoader() { + DCHECK(IsClassLoader<kVerifyFlags>()); + return down_cast<mirror::ClassLoader*>(this); +} + template <const bool kVisitClass, VerifyObjectFlags kVerifyFlags, typename Visitor, typename JavaLangRefVisitor> inline void Object::VisitReferences(const Visitor& visitor, @@ -1010,6 +1023,9 @@ inline void Object::VisitReferences(const Visitor& visitor, } else if (kVisitClass) { visitor(this, ClassOffset(), false); } + } else if (klass->IsClassLoaderClass()) { + mirror::ClassLoader* class_loader = AsClassLoader<kVerifyFlags>(); + class_loader->VisitReferences<kVisitClass, kVerifyFlags>(klass, visitor); } else { DCHECK(!klass->IsVariableSize()); VisitInstanceFieldsReferences<kVisitClass>(klass, visitor); diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index eea9f3751e..4967a14a39 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -37,6 +37,7 @@ namespace mirror { class Array; class Class; +class ClassLoader; class FinalizerReference; template<class T> class ObjectArray; template<class T> class PrimitiveArray; @@ -156,6 +157,11 @@ class MANAGED LOCKABLE Object { template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> ObjectArray<T>* AsObjectArray() SHARED_REQUIRES(Locks::mutator_lock_); + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + bool IsClassLoader() SHARED_REQUIRES(Locks::mutator_lock_); + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> + ClassLoader* AsClassLoader() SHARED_REQUIRES(Locks::mutator_lock_); + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, ReadBarrierOption kReadBarrierOption = kWithReadBarrier> bool IsArrayInstance() SHARED_REQUIRES(Locks::mutator_lock_); diff --git a/runtime/modifiers.h b/runtime/modifiers.h index 8586dd196b..8b363a686f 100644 --- a/runtime/modifiers.h +++ b/runtime/modifiers.h @@ -55,6 +55,9 @@ static constexpr uint32_t kAccDontInline = 0x00400000; // method (dex // Special runtime-only flags. // Note: if only kAccClassIsReference is set, we have a soft reference. +// class is ClassLoader or one of its subclasses +static constexpr uint32_t kAccClassIsClassLoaderClass = 0x10000000; + // class/ancestor overrides finalize() static constexpr uint32_t kAccClassIsFinalizable = 0x80000000; // class is a soft/weak/phantom ref diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index 62d1e84b7e..d449f42701 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -78,6 +78,13 @@ ThreadList::~ThreadList() { Runtime::Current()->DetachCurrentThread(); } WaitForOtherNonDaemonThreadsToExit(); + // Disable GC and wait for GC to complete in case there are still daemon threads doing + // allocations. + gc::Heap* const heap = Runtime::Current()->GetHeap(); + heap->DisableGCForShutdown(); + // In case a GC is in progress, wait for it to finish. + heap->WaitForGcToComplete(gc::kGcCauseBackground, Thread::Current()); + // TODO: there's an unaddressed race here where a thread may attach during shutdown, see // Thread::Init. SuspendAllDaemonThreads(); diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 0181e5b7cc..16615340bd 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -107,7 +107,7 @@ ALWAYS_INLINE static inline bool FailOrAbort(MethodVerifier* verifier, bool cond } static void SafelyMarkAllRegistersAsConflicts(MethodVerifier* verifier, RegisterLine* reg_line) { - if (verifier->IsConstructor()) { + if (verifier->IsInstanceConstructor()) { // Before we mark all regs as conflicts, check that we don't have an uninitialized this. reg_line->CheckConstructorReturn(verifier); } @@ -1373,9 +1373,15 @@ bool MethodVerifier::SetTypesFromSignature() { // argument as uninitialized. This restricts field access until the superclass constructor is // called. const RegType& declaring_class = GetDeclaringClass(); - if (IsConstructor() && !declaring_class.IsJavaLangObject()) { - reg_line->SetRegisterType(this, arg_start + cur_arg, - reg_types_.UninitializedThisArgument(declaring_class)); + if (IsConstructor()) { + if (declaring_class.IsJavaLangObject()) { + // "this" is implicitly initialized. + reg_line->SetThisInitialized(); + reg_line->SetRegisterType(this, arg_start + cur_arg, declaring_class); + } else { + reg_line->SetRegisterType(this, arg_start + cur_arg, + reg_types_.UninitializedThisArgument(declaring_class)); + } } else { reg_line->SetRegisterType(this, arg_start + cur_arg, declaring_class); } @@ -1698,16 +1704,6 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { std::unique_ptr<RegisterLine> branch_line; std::unique_ptr<RegisterLine> fallthrough_line; - /* - * If we are in a constructor, and we currently have an UninitializedThis type - * in a register somewhere, we need to make sure it isn't overwritten. - */ - bool track_uninitialized_this = false; - size_t uninitialized_this_loc = 0; - if (IsConstructor()) { - track_uninitialized_this = work_line_->GetUninitializedThisLoc(this, &uninitialized_this_loc); - } - switch (inst->Opcode()) { case Instruction::NOP: /* @@ -1785,14 +1781,14 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { break; } case Instruction::RETURN_VOID: - if (!IsConstructor() || work_line_->CheckConstructorReturn(this)) { + if (!IsInstanceConstructor() || work_line_->CheckConstructorReturn(this)) { if (!GetMethodReturnType().IsConflict()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-void not expected"; } } break; case Instruction::RETURN: - if (!IsConstructor() || work_line_->CheckConstructorReturn(this)) { + if (!IsInstanceConstructor() || work_line_->CheckConstructorReturn(this)) { /* check the method signature */ const RegType& return_type = GetMethodReturnType(); if (!return_type.IsCategory1Types()) { @@ -1817,7 +1813,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } break; case Instruction::RETURN_WIDE: - if (!IsConstructor() || work_line_->CheckConstructorReturn(this)) { + if (!IsInstanceConstructor() || work_line_->CheckConstructorReturn(this)) { /* check the method signature */ const RegType& return_type = GetMethodReturnType(); if (!return_type.IsCategory2Types()) { @@ -1833,7 +1829,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } break; case Instruction::RETURN_OBJECT: - if (!IsConstructor() || work_line_->CheckConstructorReturn(this)) { + if (!IsInstanceConstructor() || work_line_->CheckConstructorReturn(this)) { const RegType& return_type = GetMethodReturnType(); if (!return_type.IsReferenceTypes()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-object not expected"; @@ -3003,20 +2999,6 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { */ } // end - switch (dec_insn.opcode) - /* - * If we are in a constructor, and we had an UninitializedThis type - * in a register somewhere, we need to make sure it wasn't overwritten. - */ - if (track_uninitialized_this) { - bool was_invoke_direct = (inst->Opcode() == Instruction::INVOKE_DIRECT || - inst->Opcode() == Instruction::INVOKE_DIRECT_RANGE); - if (work_line_->WasUninitializedThisOverwritten(this, uninitialized_this_loc, - was_invoke_direct)) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) - << "Constructor failed to initialize this object"; - } - } - if (have_pending_hard_failure_) { if (Runtime::Current()->IsAotCompiler()) { /* When AOT compiling, check that the last failure is a hard failure */ @@ -4378,6 +4360,10 @@ bool MethodVerifier::UpdateRegisters(uint32_t next_insn, RegisterLine* merge_lin const Instruction* ret_inst = Instruction::At(code_item_->insns_ + next_insn); Instruction::Code opcode = ret_inst->Opcode(); if (opcode == Instruction::RETURN_VOID || opcode == Instruction::RETURN_VOID_NO_BARRIER) { + // Explicitly copy the this-initialized flag from the merge-line, as we didn't copy its + // state. Must be done before SafelyMarkAllRegistersAsConflicts as that will do the + // super-constructor-call checking. + target_line->CopyThisInitialized(*merge_line); SafelyMarkAllRegistersAsConflicts(this, target_line); } else { target_line->CopyFromLine(merge_line); diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 3b59bba506..21f8543b38 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -283,6 +283,10 @@ class MethodVerifier { return (method_access_flags_ & kAccStatic) != 0; } + bool IsInstanceConstructor() const { + return IsConstructor() && !IsStatic(); + } + SafeMap<uint32_t, std::set<uint32_t>>& GetStringInitPcRegMap() { return string_init_pc_reg_map_; } diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc index 2838681f4f..f286a453b1 100644 --- a/runtime/verifier/register_line.cc +++ b/runtime/verifier/register_line.cc @@ -18,66 +18,30 @@ #include "base/stringprintf.h" #include "dex_instruction-inl.h" -#include "method_verifier.h" +#include "method_verifier-inl.h" #include "register_line-inl.h" #include "reg_type-inl.h" namespace art { namespace verifier { -bool RegisterLine::WasUninitializedThisOverwritten(MethodVerifier* verifier, - size_t this_loc, - bool was_invoke_direct) const { - DCHECK(verifier->IsConstructor()); - - // Is the UnintializedThis type still there? - if (GetRegisterType(verifier, this_loc).IsUninitializedThisReference() || - GetRegisterType(verifier, this_loc).IsUnresolvedAndUninitializedThisReference()) { - return false; - } - - // If there is an initialized reference here now, did we just perform an invoke-direct? Note that - // this is the correct approach for dex bytecode: results of invoke-direct are stored in the - // result register. Overwriting "this_loc" can only be done by a constructor call. - if (GetRegisterType(verifier, this_loc).IsReferenceTypes() && was_invoke_direct) { - return false; - // Otherwise we could have just copied a different initialized reference to this location. - } - - // The UnintializedThis in the register is gone, so check to see if it's somewhere else now. - for (size_t i = 0; i < num_regs_; i++) { - if (GetRegisterType(verifier, i).IsUninitializedThisReference() || - GetRegisterType(verifier, i).IsUnresolvedAndUninitializedThisReference()) { - // We found it somewhere else... - return false; - } - } - - // The UninitializedThis is gone from the original register, and now we can't find it. - return true; -} - -bool RegisterLine::GetUninitializedThisLoc(MethodVerifier* verifier, size_t* vreg) const { - for (size_t i = 0; i < num_regs_; i++) { - if (GetRegisterType(verifier, i).IsUninitializedThisReference() || - GetRegisterType(verifier, i).IsUnresolvedAndUninitializedThisReference()) { - *vreg = i; - return true; - } - } - return false; -} - bool RegisterLine::CheckConstructorReturn(MethodVerifier* verifier) const { - for (size_t i = 0; i < num_regs_; i++) { - if (GetRegisterType(verifier, i).IsUninitializedThisReference() || - GetRegisterType(verifier, i).IsUnresolvedAndUninitializedThisReference()) { - verifier->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) - << "Constructor returning without calling superclass constructor"; - return false; + if (kIsDebugBuild && this_initialized_) { + // Ensure that there is no UninitializedThisReference type anymore if this_initialized_ is true. + for (size_t i = 0; i < num_regs_; i++) { + const RegType& type = GetRegisterType(verifier, i); + CHECK(!type.IsUninitializedThisReference() && + !type.IsUnresolvedAndUninitializedThisReference()) + << i << ": " << type.IsUninitializedThisReference() << " in " + << PrettyMethod(verifier->GetMethodReference().dex_method_index, + *verifier->GetMethodReference().dex_file); } } - return true; + if (!this_initialized_) { + verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) + << "Constructor returning without calling superclass constructor"; + } + return this_initialized_; } const RegType& RegisterLine::GetInvocationThis(MethodVerifier* verifier, const Instruction* inst, @@ -148,6 +112,11 @@ void RegisterLine::MarkRefsAsInitialized(MethodVerifier* verifier, const RegType } } } + // Is this initializing "this"? + if (uninit_type.IsUninitializedThisReference() || + uninit_type.IsUnresolvedAndUninitializedThisReference()) { + this_initialized_ = true; + } DCHECK_GT(changed, 0u); } @@ -432,6 +401,11 @@ bool RegisterLine::MergeRegisters(MethodVerifier* verifier, const RegisterLine* } } } + // Check whether "this" was initialized in both paths. + if (this_initialized_ && !incoming_line->this_initialized_) { + this_initialized_ = false; + changed = true; + } return changed; } diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h index 4fb3a2c99a..f61e51fb23 100644 --- a/runtime/verifier/register_line.h +++ b/runtime/verifier/register_line.h @@ -114,6 +114,7 @@ class RegisterLine { memcpy(&line_, &src->line_, num_regs_ * sizeof(uint16_t)); monitors_ = src->monitors_; reg_to_lock_depths_ = src->reg_to_lock_depths_; + this_initialized_ = src->this_initialized_; } std::string Dump(MethodVerifier* verifier) const SHARED_REQUIRES(Locks::mutator_lock_); @@ -149,6 +150,14 @@ class RegisterLine { void MarkAllRegistersAsConflictsExcept(MethodVerifier* verifier, uint32_t vsrc); void MarkAllRegistersAsConflictsExceptWide(MethodVerifier* verifier, uint32_t vsrc); + void SetThisInitialized() { + this_initialized_ = true; + } + + void CopyThisInitialized(const RegisterLine& src) { + this_initialized_ = src.this_initialized_; + } + /* * Check constraints on constructor return. Specifically, make sure that the "this" argument got * initialized. @@ -158,18 +167,6 @@ class RegisterLine { */ bool CheckConstructorReturn(MethodVerifier* verifier) const; - /* - * Check if an UninitializedThis at the specified location has been overwritten before - * being correctly initialized. - */ - bool WasUninitializedThisOverwritten(MethodVerifier* verifier, size_t this_loc, - bool was_invoke_direct) const; - - /* - * Get the first location of an UninitializedThis type, or return kInvalidVreg if there are none. - */ - bool GetUninitializedThisLoc(MethodVerifier* verifier, size_t* vreg) const; - // Compare two register lines. Returns 0 if they match. // Using this for a sort is unwise, since the value can change based on machine endianness. int CompareLine(const RegisterLine* line2) const { @@ -354,7 +351,7 @@ class RegisterLine { } RegisterLine(size_t num_regs, MethodVerifier* verifier) - : num_regs_(num_regs) { + : num_regs_(num_regs), this_initialized_(false) { memset(&line_, 0, num_regs_ * sizeof(uint16_t)); SetResultTypeToUnknown(verifier); } @@ -372,6 +369,9 @@ class RegisterLine { // monitor-enter on v5 and then on v6, we expect the monitor-exit to be on v6 then on v5. AllocationTrackingSafeMap<uint32_t, uint32_t, kAllocatorTagVerifier> reg_to_lock_depths_; + // Whether "this" initialization (a constructor supercall) has happened. + bool this_initialized_; + // An array of RegType Ids associated with each dex register. uint16_t line_[0]; diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt index 884f280a12..e2101a66e5 100644 --- a/test/800-smali/expected.txt +++ b/test/800-smali/expected.txt @@ -38,4 +38,5 @@ b/22411633 (4) b/22411633 (5) b/22777307 b/22881413 +b/20843113 Done! diff --git a/test/800-smali/smali/b_20843113.smali b/test/800-smali/smali/b_20843113.smali new file mode 100644 index 0000000000..ab3dc4157b --- /dev/null +++ b/test/800-smali/smali/b_20843113.smali @@ -0,0 +1,34 @@ +.class public LB20843113; +.super Ljava/lang/Object; + + +.method public constructor <init>(I)V +.registers 2 + +:Label1 + # An instruction that may throw, so as to pass UninitializedThis to the handler + div-int v1, v1, v1 + + # Call the super-constructor + invoke-direct {v0}, Ljava/lang/Object;-><init>()V + + # Return normally. + return-void + +:Label2 + + +:Handler + move-exception v0 # Overwrite the (last) "this" register. This should be + # allowed as we will terminate abnormally below. + + throw v0 # Terminate abnormally + +.catchall {:Label1 .. :Label2} :Handler +.end method + +# Just a dummy. +.method public static run()V +.registers 1 + return-void +.end method diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java index e1ac7493ae..3c88040f0f 100644 --- a/test/800-smali/src/Main.java +++ b/test/800-smali/src/Main.java @@ -122,6 +122,7 @@ public class Main { testCases.add(new TestCase("b/22777307", "B22777307", "run", null, new InstantiationError(), null)); testCases.add(new TestCase("b/22881413", "B22881413", "run", null, null, null)); + testCases.add(new TestCase("b/20843113", "B20843113", "run", null, null, null)); } public void runTests() { |