ART: Fix forbidden thread state change in interpreter

While loading a type for assignability, it might happen that it's
not available yet locally and must be resolved. Formerly, we
disallowed a state change to ensure no GC taking place while a new
shadow frame has not been pushed on the stack yet.

As a fix, allow a "shadow frame under construction" in the thread,
which is visited during GC.

Change-Id: I973487a46b0e9e21fd6d49099d713b58f06d3b45
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 63ae6fd..19b85e4 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -74,7 +74,13 @@
   // Initialize new shadow frame.
   const size_t first_dest_reg = num_regs - num_ins;
   if (do_assignability_check) {
-    // Slow path: we need to do runtime check on reference assignment. We need to load the shorty
+    // Slow path.
+    // We might need to do class loading, which incurs a thread state change to kNative. So
+    // register the shadow frame as under construction and allow suspension again.
+    self->SetShadowFrameUnderConstruction(new_shadow_frame);
+    self->EndAssertNoThreadSuspension(old_cause);
+
+    // We need to do runtime check on reference assignment. We need to load the shorty
     // to get the exact type of each reference argument.
     const DexFile::TypeList* params = mh.GetParameterTypeList();
     const char* shorty = mh.GetShorty();
@@ -107,11 +113,9 @@
             Class* arg_type = mh.GetClassFromTypeIdx(params->GetTypeItem(shorty_pos).type_idx_);
             if (arg_type == NULL) {
               CHECK(self->IsExceptionPending());
-              self->EndAssertNoThreadSuspension(old_cause);
               return false;
             }
             if (!o->VerifierInstanceOf(arg_type)) {
-              self->EndAssertNoThreadSuspension(old_cause);
               // This should never happen.
               self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
                                        "Ljava/lang/VirtualMachineError;",
@@ -138,6 +142,8 @@
           break;
       }
     }
+    // We're done with the construction.
+    self->ClearShadowFrameUnderConstruction();
   } else {
     // Fast path: no extra checks.
     if (is_range) {
@@ -158,8 +164,8 @@
         AssignRegister(new_shadow_frame, shadow_frame, first_dest_reg + arg_index, regList & 0x0f);
       }
     }
+    self->EndAssertNoThreadSuspension(old_cause);
   }
-  self->EndAssertNoThreadSuspension(old_cause);
 
   // Do the call now.
   if (LIKELY(Runtime::Current()->IsStarted())) {
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 6ab0d1e..96834b8 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -22,7 +22,7 @@
 namespace art {
 
 const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
-const uint8_t OatHeader::kOatVersion[] = { '0', '3', '2', '\0' };
+const uint8_t OatHeader::kOatVersion[] = { '0', '3', '3', '\0' };
 
 OatHeader::OatHeader() {
   memset(this, 0, sizeof(*this));
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 758944c..f89fada 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -122,6 +122,16 @@
   return sf;
 }
 
+void Thread::SetShadowFrameUnderConstruction(ShadowFrame* sf) {
+  sf->SetLink(tlsPtr_.shadow_frame_under_construction);
+  tlsPtr_.shadow_frame_under_construction = sf;
+}
+
+void Thread::ClearShadowFrameUnderConstruction() {
+  CHECK_NE(static_cast<ShadowFrame*>(nullptr), tlsPtr_.shadow_frame_under_construction);
+  tlsPtr_.shadow_frame_under_construction = tlsPtr_.shadow_frame_under_construction->GetLink();
+}
+
 void Thread::InitTid() {
   tls32_.tid = ::art::GetTid();
 }
@@ -2128,6 +2138,15 @@
       mapper.VisitShadowFrame(shadow_frame);
     }
   }
+  if (tlsPtr_.shadow_frame_under_construction != nullptr) {
+    RootCallbackVisitor visitorToCallback(visitor, arg, thread_id);
+    ReferenceMapVisitor<RootCallbackVisitor> mapper(this, nullptr, visitorToCallback);
+    for (ShadowFrame* shadow_frame = tlsPtr_.shadow_frame_under_construction;
+        shadow_frame != nullptr;
+        shadow_frame = shadow_frame->GetLink()) {
+      mapper.VisitShadowFrame(shadow_frame);
+    }
+  }
   // Visit roots on this thread's stack
   Context* context = GetLongJumpContext();
   RootCallbackVisitor visitorToCallback(visitor, arg, thread_id);
diff --git a/runtime/thread.h b/runtime/thread.h
index 6569a96..8ac106b 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -711,6 +711,13 @@
     return tlsPtr_.deoptimization_shadow_frame != nullptr;
   }
 
+  void SetShadowFrameUnderConstruction(ShadowFrame* sf);
+  void ClearShadowFrameUnderConstruction();
+
+  bool HasShadowFrameUnderConstruction() const {
+    return tlsPtr_.shadow_frame_under_construction != nullptr;
+  }
+
   std::deque<instrumentation::InstrumentationStackFrame>* GetInstrumentationStack() {
     return tlsPtr_.instrumentation_stack;
   }
@@ -962,8 +969,8 @@
       stack_trace_sample(nullptr), wait_next(nullptr), monitor_enter_object(nullptr),
       top_handle_scope(nullptr), class_loader_override(nullptr), long_jump_context(nullptr),
       instrumentation_stack(nullptr), debug_invoke_req(nullptr), single_step_control(nullptr),
-      deoptimization_shadow_frame(nullptr), name(nullptr), pthread_self(0),
-      last_no_thread_suspension_cause(nullptr), thread_local_start(nullptr),
+      deoptimization_shadow_frame(nullptr), shadow_frame_under_construction(nullptr), name(nullptr),
+      pthread_self(0), last_no_thread_suspension_cause(nullptr), thread_local_start(nullptr),
       thread_local_pos(nullptr), thread_local_end(nullptr), thread_local_objects(0),
       thread_local_alloc_stack_top(nullptr), thread_local_alloc_stack_end(nullptr) {
     }
@@ -1039,6 +1046,9 @@
     // Shadow frame stack that is used temporarily during the deoptimization of a method.
     ShadowFrame* deoptimization_shadow_frame;
 
+    // Shadow frame stack that is currently under construction but not yet on the stack
+    ShadowFrame* shadow_frame_under_construction;
+
     // A cached copy of the java.lang.Thread's name.
     std::string* name;