Replace StackHandleScopeCollection with VariableSizedHandleScope

VariableSizedHandleScope's internal handle scopes are not pushed
directly on the thread. This means that it is safe to intermix with
other types of handle scopes.

Added test.

Test: clean-oat-host && test-art-host

Change-Id: Id2fd1155788428f394d49615d337d9134824c8f0
diff --git a/runtime/handle_scope-inl.h b/runtime/handle_scope-inl.h
index cceb007..b212d09 100644
--- a/runtime/handle_scope-inl.h
+++ b/runtime/handle_scope-inl.h
@@ -28,24 +28,30 @@
 namespace art {
 
 template<size_t kNumReferences>
-inline StackHandleScope<kNumReferences>::StackHandleScope(Thread* self, mirror::Object* fill_value)
-    : HandleScope(self->GetTopHandleScope(), kNumReferences), self_(self), pos_(0) {
-  DCHECK_EQ(self, Thread::Current());
+inline FixedSizeHandleScope<kNumReferences>::FixedSizeHandleScope(BaseHandleScope* link,
+                                                                  mirror::Object* fill_value)
+    : HandleScope(link, kNumReferences) {
   if (kDebugLocking) {
     Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
   }
-  static_assert(kNumReferences >= 1, "StackHandleScope must contain at least 1 reference");
-  // TODO: Figure out how to use a compile assert.
-  CHECK_EQ(&storage_[0], GetReferences());
+  static_assert(kNumReferences >= 1, "FixedSizeHandleScope must contain at least 1 reference");
+  DCHECK_EQ(&storage_[0], GetReferences());  // TODO: Figure out how to use a compile assert.
   for (size_t i = 0; i < kNumReferences; ++i) {
     SetReference(i, fill_value);
   }
+}
+
+template<size_t kNumReferences>
+inline StackHandleScope<kNumReferences>::StackHandleScope(Thread* self, mirror::Object* fill_value)
+    : FixedSizeHandleScope<kNumReferences>(self->GetTopHandleScope(), fill_value),
+      self_(self) {
+  DCHECK_EQ(self, Thread::Current());
   self_->PushHandleScope(this);
 }
 
 template<size_t kNumReferences>
 inline StackHandleScope<kNumReferences>::~StackHandleScope() {
-  HandleScope* top_handle_scope = self_->PopHandleScope();
+  BaseHandleScope* top_handle_scope = self_->PopHandleScope();
   DCHECK_EQ(top_handle_scope, this);
   if (kDebugLocking) {
     Locks::mutator_lock_->AssertSharedHeld(self_);
@@ -66,7 +72,7 @@
 }
 
 inline mirror::Object* HandleScope::GetReference(size_t i) const {
-  DCHECK_LT(i, number_of_references_);
+  DCHECK_LT(i, NumberOfReferences());
   if (kDebugLocking) {
     Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
   }
@@ -74,12 +80,12 @@
 }
 
 inline Handle<mirror::Object> HandleScope::GetHandle(size_t i) {
-  DCHECK_LT(i, number_of_references_);
+  DCHECK_LT(i, NumberOfReferences());
   return Handle<mirror::Object>(&GetReferences()[i]);
 }
 
 inline MutableHandle<mirror::Object> HandleScope::GetMutableHandle(size_t i) {
-  DCHECK_LT(i, number_of_references_);
+  DCHECK_LT(i, NumberOfReferences());
   return MutableHandle<mirror::Object>(&GetReferences()[i]);
 }
 
@@ -87,7 +93,7 @@
   if (kDebugLocking) {
     Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
   }
-  DCHECK_LT(i, number_of_references_);
+  DCHECK_LT(i, NumberOfReferences());
   GetReferences()[i].Assign(object);
 }
 
@@ -95,13 +101,13 @@
   // A HandleScope should always contain something. One created by the
   // jni_compiler should have a jobject/jclass as a native method is
   // passed in a this pointer or a class
-  DCHECK_GT(number_of_references_, 0U);
+  DCHECK_GT(NumberOfReferences(), 0U);
   return &GetReferences()[0] <= handle_scope_entry &&
       handle_scope_entry <= &GetReferences()[number_of_references_ - 1];
 }
 
 template<size_t kNumReferences> template<class T>
-inline MutableHandle<T> StackHandleScope<kNumReferences>::NewHandle(T* object) {
+inline MutableHandle<T> FixedSizeHandleScope<kNumReferences>::NewHandle(T* object) {
   SetReference(pos_, object);
   MutableHandle<T> h(GetHandle<T>(pos_));
   pos_++;
@@ -109,24 +115,24 @@
 }
 
 template<size_t kNumReferences> template<class MirrorType, bool kPoison>
-inline MutableHandle<MirrorType> StackHandleScope<kNumReferences>::NewHandle(
+inline MutableHandle<MirrorType> FixedSizeHandleScope<kNumReferences>::NewHandle(
     ObjPtr<MirrorType, kPoison> object) {
   return NewHandle(object.Ptr());
 }
 
 template<size_t kNumReferences> template<class T>
-inline HandleWrapper<T> StackHandleScope<kNumReferences>::NewHandleWrapper(T** object) {
+inline HandleWrapper<T> FixedSizeHandleScope<kNumReferences>::NewHandleWrapper(T** object) {
   return HandleWrapper<T>(object, NewHandle(*object));
 }
 
 template<size_t kNumReferences> template<class T>
-inline HandleWrapperObjPtr<T> StackHandleScope<kNumReferences>::NewHandleWrapper(
+inline HandleWrapperObjPtr<T> FixedSizeHandleScope<kNumReferences>::NewHandleWrapper(
     ObjPtr<T>* object) {
   return HandleWrapperObjPtr<T>(object, NewHandle(*object));
 }
 
 template<size_t kNumReferences>
-inline void StackHandleScope<kNumReferences>::SetReference(size_t i, mirror::Object* object) {
+inline void FixedSizeHandleScope<kNumReferences>::SetReference(size_t i, mirror::Object* object) {
   if (kDebugLocking) {
     Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
   }
@@ -135,12 +141,111 @@
   GetReferences()[i].Assign(object);
 }
 
+// Number of references contained within this handle scope.
+inline uint32_t BaseHandleScope::NumberOfReferences() const {
+  return LIKELY(!IsVariableSized())
+      ? AsHandleScope()->NumberOfReferences()
+      : AsVariableSized()->NumberOfReferences();
+}
+
+inline bool BaseHandleScope::Contains(StackReference<mirror::Object>* handle_scope_entry) const {
+  return LIKELY(!IsVariableSized())
+      ? AsHandleScope()->Contains(handle_scope_entry)
+      : AsVariableSized()->Contains(handle_scope_entry);
+}
+
+template <typename Visitor>
+inline void BaseHandleScope::VisitRoots(Visitor& visitor) {
+  if (LIKELY(!IsVariableSized())) {
+    AsHandleScope()->VisitRoots(visitor);
+  } else {
+    AsVariableSized()->VisitRoots(visitor);
+  }
+}
+
+inline VariableSizedHandleScope* BaseHandleScope::AsVariableSized() {
+  DCHECK(IsVariableSized());
+  return down_cast<VariableSizedHandleScope*>(this);
+}
+
+inline HandleScope* BaseHandleScope::AsHandleScope() {
+  DCHECK(!IsVariableSized());
+  return down_cast<HandleScope*>(this);
+}
+
+inline const VariableSizedHandleScope* BaseHandleScope::AsVariableSized() const {
+  DCHECK(IsVariableSized());
+  return down_cast<const VariableSizedHandleScope*>(this);
+}
+
+inline const HandleScope* BaseHandleScope::AsHandleScope() const {
+  DCHECK(!IsVariableSized());
+  return down_cast<const HandleScope*>(this);
+}
+
+template<class T>
+MutableHandle<T> VariableSizedHandleScope::NewHandle(T* object) {
+  if (current_scope_->RemainingSlots() == 0) {
+    current_scope_ = new LocalScopeType(current_scope_);
+  }
+  return current_scope_->NewHandle(object);
+}
+
 template<class MirrorType, bool kPoison>
-inline MutableHandle<MirrorType> StackHandleScopeCollection::NewHandle(
+inline MutableHandle<MirrorType> VariableSizedHandleScope::NewHandle(
     ObjPtr<MirrorType, kPoison> ptr) {
   return NewHandle(ptr.Ptr());
 }
 
+inline VariableSizedHandleScope::VariableSizedHandleScope(Thread* const self)
+    : BaseHandleScope(self->GetTopHandleScope()),
+      self_(self) {
+  current_scope_ = new LocalScopeType(/*link*/ nullptr);
+  self_->PushHandleScope(this);
+}
+
+inline VariableSizedHandleScope::~VariableSizedHandleScope() {
+  BaseHandleScope* top_handle_scope = self_->PopHandleScope();
+  DCHECK_EQ(top_handle_scope, this);
+  while (current_scope_ != nullptr) {
+    LocalScopeType* next = reinterpret_cast<LocalScopeType*>(current_scope_->GetLink());
+    delete current_scope_;
+    current_scope_ = next;
+  }
+}
+
+inline uint32_t VariableSizedHandleScope::NumberOfReferences() const {
+  uint32_t sum = 0;
+  const LocalScopeType* cur = current_scope_;
+  while (cur != nullptr) {
+    sum += cur->NumberOfReferences();
+    cur = reinterpret_cast<const LocalScopeType*>(cur->GetLink());
+  }
+  return sum;
+}
+
+inline bool VariableSizedHandleScope::Contains(StackReference<mirror::Object>* handle_scope_entry)
+    const {
+  const LocalScopeType* cur = current_scope_;
+  while (cur != nullptr) {
+    if (cur->Contains(handle_scope_entry)) {
+      return true;
+    }
+    cur = reinterpret_cast<const LocalScopeType*>(cur->GetLink());
+  }
+  return false;
+}
+
+template <typename Visitor>
+inline void VariableSizedHandleScope::VisitRoots(Visitor& visitor) {
+  LocalScopeType* cur = current_scope_;
+  while (cur != nullptr) {
+    cur->VisitRoots(visitor);
+    cur = reinterpret_cast<LocalScopeType*>(cur->GetLink());
+  }
+}
+
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_HANDLE_SCOPE_INL_H_