summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/driver/compiler_driver.cc24
-rw-r--r--runtime/class_linker.cc18
-rw-r--r--runtime/class_table-inl.h44
-rw-r--r--runtime/class_table.cc10
-rw-r--r--runtime/class_table.h11
-rw-r--r--runtime/gc/collector/mark_sweep.cc2
-rw-r--r--runtime/gc/heap.cc18
-rw-r--r--runtime/gc/heap.h6
-rw-r--r--runtime/hprof/hprof.cc36
-rw-r--r--runtime/mirror/class.h9
-rw-r--r--runtime/mirror/class_loader-inl.h43
-rw-r--r--runtime/mirror/class_loader.h8
-rw-r--r--runtime/mirror/object-inl.h16
-rw-r--r--runtime/mirror/object.h6
-rw-r--r--runtime/modifiers.h3
-rw-r--r--runtime/thread_list.cc7
-rw-r--r--runtime/verifier/method_verifier.cc50
-rw-r--r--runtime/verifier/method_verifier.h4
-rw-r--r--runtime/verifier/register_line.cc76
-rw-r--r--runtime/verifier/register_line.h26
-rw-r--r--test/800-smali/expected.txt1
-rw-r--r--test/800-smali/smali/b_20843113.smali34
-rw-r--r--test/800-smali/src/Main.java1
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() {