Merge "Compiler: Take advantage of constant propagation" into dalvik-dev
diff --git a/src/debugger.cc b/src/debugger.cc
index 672b660..87e9c72 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -224,6 +224,7 @@
 }
 
 static Thread* DecodeThread(ScopedObjectAccessUnchecked& soa, JDWP::ObjectId threadId)
+    EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_list_lock_)
     LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   Object* thread_peer = gRegistry->Get<Object*>(threadId);
@@ -1334,9 +1335,8 @@
 }
 
 bool Dbg::GetThreadName(JDWP::ObjectId threadId, std::string& name) {
-  Thread* self = Thread::Current();
-  MutexLock mu(self, *Locks::thread_list_lock_);
-  ScopedObjectAccessUnchecked soa(self);
+  ScopedObjectAccessUnchecked soa(Thread::Current());
+  MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
   Thread* thread = DecodeThread(soa, threadId);
   if (thread == NULL) {
     return false;
@@ -1581,6 +1581,7 @@
 
 int Dbg::GetThreadFrameCount(JDWP::ObjectId threadId) {
   ScopedObjectAccess soa(Thread::Current());
+  MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
   return GetStackDepth(DecodeThread(soa, threadId));
 }
 
@@ -1624,6 +1625,7 @@
   };
 
   ScopedObjectAccessUnchecked soa(Thread::Current());
+  MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
   Thread* thread = DecodeThread(soa, thread_id);  // Caller already checked thread is suspended.
   GetFrameVisitor visitor(thread->GetManagedStack(), thread->GetInstrumentationStack(), start_frame, frame_count, buf);
   visitor.WalkStack();
@@ -1669,8 +1671,11 @@
 void Dbg::ResumeThread(JDWP::ObjectId threadId) {
   ScopedObjectAccessUnchecked soa(Thread::Current());
   Object* peer = gRegistry->Get<Object*>(threadId);
-  MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
-  Thread* thread = Thread::FromManagedThread(soa, peer);
+  Thread* thread;
+  {
+    MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
+    thread = Thread::FromManagedThread(soa, peer);
+  }
   if (thread == NULL) {
     LOG(WARNING) << "No such thread for resume: " << peer;
     return;
@@ -1878,6 +1883,7 @@
   };
 
   ScopedObjectAccessUnchecked soa(Thread::Current());
+  MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
   Thread* thread = DecodeThread(soa, threadId);
   UniquePtr<Context> context(Context::Create());
   GetLocalVisitor visitor(thread->GetManagedStack(), thread->GetInstrumentationStack(), context.get(),
@@ -1961,6 +1967,7 @@
   };
 
   ScopedObjectAccessUnchecked soa(Thread::Current());
+  MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
   Thread* thread = DecodeThread(soa, threadId);
   UniquePtr<Context> context(Context::Create());
   SetLocalVisitor visitor(thread->GetManagedStack(), thread->GetInstrumentationStack(), context.get(),
@@ -2165,12 +2172,13 @@
 JDWP::JdwpError Dbg::ConfigureStep(JDWP::ObjectId threadId, JDWP::JdwpStepSize step_size,
                                    JDWP::JdwpStepDepth step_depth) {
   ScopedObjectAccessUnchecked soa(Thread::Current());
+  MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
   Thread* thread = DecodeThread(soa, threadId);
   if (thread == NULL) {
     return JDWP::ERR_INVALID_THREAD;
   }
 
-  MutexLock mu(soa.Self(), gBreakpointsLock);
+  MutexLock mu2(soa.Self(), gBreakpointsLock);
   // TODO: there's no theoretical reason why we couldn't support single-stepping
   // of multiple threads at once, but we never did so historically.
   if (gSingleStepControl.thread != NULL && thread != gSingleStepControl.thread) {
diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc
index 5aa4c2d..f4091be 100644
--- a/src/interpreter/interpreter.cc
+++ b/src/interpreter/interpreter.cc
@@ -405,11 +405,6 @@
   } else {
     UnstartedRuntimeInvoke(self, target_method, receiver, arg_array.get(), result);
   }
-  // Check the return type if the result is non-null. We do the GetReturnType
-  // after the null check to avoid resolution when there's an exception pending.
-  if (result->GetL() != NULL && !mh.GetReturnType()->IsPrimitive()) {
-    CHECK(mh.GetReturnType()->IsAssignableFrom(result->GetL()->GetClass()));
-  }
   mh.ChangeMethod(shadow_frame.GetMethod());
 }
 
@@ -768,24 +763,48 @@
         bool is_range = (dec_insn.opcode == Instruction::FILLED_NEW_ARRAY_RANGE);
         int32_t length = dec_insn.vA;
         CHECK(is_range || length <= 5);
+        if (UNLIKELY(length < 0)) {
+          self->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d", length);
+          break;
+        }
         Class* arrayClass = ResolveVerifyAndClinit(dec_insn.vB, shadow_frame.GetMethod(), self, false, true);
+        if (UNLIKELY(arrayClass == NULL)) {
+          CHECK(self->IsExceptionPending());
+          break;
+        }
         CHECK(arrayClass->IsArrayClass());
-        if (arrayClass->GetComponentType()->IsPrimitiveInt()) {
-          IntArray* newArray = IntArray::Alloc(self, length);
-          if (newArray != NULL) {
-            for (int32_t i = 0; i < length; ++i) {
-              if (is_range) {
-                newArray->Set(i, shadow_frame.GetVReg(dec_insn.vC + i));
+        Class* componentClass = arrayClass->GetComponentType();
+        if (UNLIKELY(componentClass->IsPrimitive() && !componentClass->IsPrimitiveInt())) {
+          if (componentClass->IsPrimitiveLong() || componentClass->IsPrimitiveDouble()) {
+            self->ThrowNewExceptionF("Ljava/lang/RuntimeException;",
+                                     "Bad filled array request for type %s",
+                                     PrettyDescriptor(componentClass).c_str());
+          } else {
+            self->ThrowNewExceptionF("Ljava/lang/InternalError;",
+                                     "Found type %s; filled-new-array not implemented for anything but \'int\'",
+                                     PrettyDescriptor(componentClass).c_str());
+          }
+          break;
+        }
+        Object* newArray = Array::Alloc(self, arrayClass, length);
+        if (newArray != NULL) {
+          for (int32_t i = 0; i < length; ++i) {
+            if (is_range) {
+              if (componentClass->IsPrimitiveInt()) {
+                newArray->AsIntArray()->Set(i, shadow_frame.GetVReg(dec_insn.vC + i));
               } else {
-                newArray->Set(i, shadow_frame.GetVReg(dec_insn.arg[i]));
+                newArray->AsObjectArray<Object>()->Set(i, shadow_frame.GetVRegReference(dec_insn.vC + i));
+              }
+            } else {
+              if (componentClass->IsPrimitiveInt()) {
+                newArray->AsIntArray()->Set(i, shadow_frame.GetVReg(dec_insn.arg[i]));
+              } else {
+                newArray->AsObjectArray<Object>()->Set(i, shadow_frame.GetVRegReference(dec_insn.arg[i]));
               }
             }
           }
-          result_register.SetL(newArray);
-        } else {
-          UNIMPLEMENTED(FATAL) << inst->DumpString(&mh.GetDexFile())
-              << " for array type: " << PrettyDescriptor(arrayClass);
         }
+        result_register.SetL(newArray);
         break;
       }
       case Instruction::CMPL_FLOAT: {
@@ -1287,13 +1306,13 @@
         shadow_frame.SetVReg(dec_insn.vA, -shadow_frame.GetVReg(dec_insn.vB));
         break;
       case Instruction::NOT_INT:
-        shadow_frame.SetVReg(dec_insn.vA, 0 ^ shadow_frame.GetVReg(dec_insn.vB));
+        shadow_frame.SetVReg(dec_insn.vA, ~shadow_frame.GetVReg(dec_insn.vB));
         break;
       case Instruction::NEG_LONG:
         shadow_frame.SetVRegLong(dec_insn.vA, -shadow_frame.GetVRegLong(dec_insn.vB));
         break;
       case Instruction::NOT_LONG:
-        shadow_frame.SetVRegLong(dec_insn.vA, 0 ^ shadow_frame.GetVRegLong(dec_insn.vB));
+        shadow_frame.SetVRegLong(dec_insn.vA, ~shadow_frame.GetVRegLong(dec_insn.vB));
         break;
       case Instruction::NEG_FLOAT:
         shadow_frame.SetVRegFloat(dec_insn.vA, -shadow_frame.GetVRegFloat(dec_insn.vB));
@@ -1407,17 +1426,17 @@
                     shadow_frame.GetVReg(dec_insn.vC));
         break;
       case Instruction::SHL_INT:
-        shadow_frame.SetVReg(dec_insn.vA,
-                             shadow_frame.GetVReg(dec_insn.vB) << shadow_frame.GetVReg(dec_insn.vC));
+        shadow_frame.SetVReg(dec_insn.vA, shadow_frame.GetVReg(dec_insn.vB) <<
+                             (shadow_frame.GetVReg(dec_insn.vC) & 0x1f));
         break;
       case Instruction::SHR_INT:
-        shadow_frame.SetVReg(dec_insn.vA,
-                             shadow_frame.GetVReg(dec_insn.vB) >> shadow_frame.GetVReg(dec_insn.vC));
+        shadow_frame.SetVReg(dec_insn.vA, shadow_frame.GetVReg(dec_insn.vB) >>
+                             (shadow_frame.GetVReg(dec_insn.vC) & 0x1f));
         break;
       case Instruction::USHR_INT:
         shadow_frame.SetVReg(dec_insn.vA,
                              static_cast<uint32_t>(shadow_frame.GetVReg(dec_insn.vB)) >>
-                             shadow_frame.GetVReg(dec_insn.vC));
+                             (shadow_frame.GetVReg(dec_insn.vC) & 0x1f));
         break;
       case Instruction::AND_INT:
         shadow_frame.SetVReg(dec_insn.vA,
@@ -1472,17 +1491,17 @@
       case Instruction::SHL_LONG:
         shadow_frame.SetVRegLong(dec_insn.vA,
                                  shadow_frame.GetVRegLong(dec_insn.vB) <<
-                                 shadow_frame.GetVReg(dec_insn.vC));
+                                 (shadow_frame.GetVReg(dec_insn.vC) & 0x3f));
         break;
       case Instruction::SHR_LONG:
         shadow_frame.SetVRegLong(dec_insn.vA,
                                  shadow_frame.GetVRegLong(dec_insn.vB) >>
-                                 shadow_frame.GetVReg(dec_insn.vC));
+                                 (shadow_frame.GetVReg(dec_insn.vC) & 0x3f));
         break;
       case Instruction::USHR_LONG:
         shadow_frame.SetVRegLong(dec_insn.vA,
                                  static_cast<uint64_t>(shadow_frame.GetVRegLong(dec_insn.vB)) >>
-                                 shadow_frame.GetVReg(dec_insn.vC));
+                                 (shadow_frame.GetVReg(dec_insn.vC) & 0x3f));
         break;
       case Instruction::ADD_FLOAT:
         shadow_frame.SetVRegFloat(dec_insn.vA,
@@ -1551,17 +1570,17 @@
                        shadow_frame.GetVReg(dec_insn.vB));
         break;
       case Instruction::SHL_INT_2ADDR:
-        shadow_frame.SetVReg(dec_insn.vA,
-                             shadow_frame.GetVReg(dec_insn.vA) << shadow_frame.GetVReg(dec_insn.vB));
+        shadow_frame.SetVReg(dec_insn.vA, shadow_frame.GetVReg(dec_insn.vA) <<
+                             (shadow_frame.GetVReg(dec_insn.vB) & 0x1f));
         break;
       case Instruction::SHR_INT_2ADDR:
-        shadow_frame.SetVReg(dec_insn.vA,
-                             shadow_frame.GetVReg(dec_insn.vA) >> shadow_frame.GetVReg(dec_insn.vB));
+        shadow_frame.SetVReg(dec_insn.vA, shadow_frame.GetVReg(dec_insn.vA) >>
+                             (shadow_frame.GetVReg(dec_insn.vB) & 0x1f));
         break;
       case Instruction::USHR_INT_2ADDR:
         shadow_frame.SetVReg(dec_insn.vA,
                              static_cast<uint32_t>(shadow_frame.GetVReg(dec_insn.vA)) >>
-                             shadow_frame.GetVReg(dec_insn.vB));
+                             (shadow_frame.GetVReg(dec_insn.vB) & 0x1f));
         break;
       case Instruction::AND_INT_2ADDR:
         shadow_frame.SetVReg(dec_insn.vA,
@@ -1620,17 +1639,17 @@
       case Instruction::SHL_LONG_2ADDR:
         shadow_frame.SetVRegLong(dec_insn.vA,
                                  shadow_frame.GetVRegLong(dec_insn.vA) <<
-                                 shadow_frame.GetVReg(dec_insn.vB));
+                                 (shadow_frame.GetVReg(dec_insn.vB) & 0x3f));
         break;
       case Instruction::SHR_LONG_2ADDR:
         shadow_frame.SetVRegLong(dec_insn.vA,
                                  shadow_frame.GetVRegLong(dec_insn.vA) >>
-                                 shadow_frame.GetVReg(dec_insn.vB));
+                                 (shadow_frame.GetVReg(dec_insn.vB) & 0x3f));
         break;
       case Instruction::USHR_LONG_2ADDR:
         shadow_frame.SetVRegLong(dec_insn.vA,
                                  static_cast<uint64_t>(shadow_frame.GetVRegLong(dec_insn.vA)) >>
-                                 shadow_frame.GetVReg(dec_insn.vB));
+                                 (shadow_frame.GetVReg(dec_insn.vB) & 0x3f));
         break;
       case Instruction::ADD_FLOAT_2ADDR:
         shadow_frame.SetVRegFloat(dec_insn.vA,
@@ -1717,15 +1736,17 @@
         shadow_frame.SetVReg(dec_insn.vA, shadow_frame.GetVReg(dec_insn.vB) ^ dec_insn.vC);
         break;
       case Instruction::SHL_INT_LIT8:
-        shadow_frame.SetVReg(dec_insn.vA, shadow_frame.GetVReg(dec_insn.vB) << dec_insn.vC);
+        shadow_frame.SetVReg(dec_insn.vA, shadow_frame.GetVReg(dec_insn.vB) <<
+                             (dec_insn.vC & 0x1f));
         break;
       case Instruction::SHR_INT_LIT8:
-        shadow_frame.SetVReg(dec_insn.vA, shadow_frame.GetVReg(dec_insn.vB) >> dec_insn.vC);
+        shadow_frame.SetVReg(dec_insn.vA, shadow_frame.GetVReg(dec_insn.vB) >>
+                             (dec_insn.vC & 0x1f));
         break;
       case Instruction::USHR_INT_LIT8:
         shadow_frame.SetVReg(dec_insn.vA,
                              static_cast<uint32_t>(shadow_frame.GetVReg(dec_insn.vB)) >>
-                             dec_insn.vC);
+                             (dec_insn.vC & 0x1f));
         break;
       default:
         LOG(FATAL) << "Unexpected instruction: " << inst->DumpString(&mh.GetDexFile());
diff --git a/src/jdwp/jdwp.h b/src/jdwp/jdwp.h
index 3186006..fbca7d1 100644
--- a/src/jdwp/jdwp.h
+++ b/src/jdwp/jdwp.h
@@ -286,7 +286,7 @@
   explicit JdwpState(const JdwpOptions* options);
   bool InvokeInProgress();
   bool IsConnected();
-  void SuspendByPolicy(JdwpSuspendPolicy suspend_policy,  JDWP::ObjectId thread_self_id)
+  void SuspendByPolicy(JdwpSuspendPolicy suspend_policy, JDWP::ObjectId thread_self_id)
       LOCKS_EXCLUDED(Locks::mutator_lock_);
   void SendRequestAndPossiblySuspend(ExpandBuf* pReq, JdwpSuspendPolicy suspend_policy,
                                      ObjectId threadId)
diff --git a/src/jdwp/jdwp_handler.cc b/src/jdwp/jdwp_handler.cc
index 07e47b5..88677d5 100644
--- a/src/jdwp/jdwp_handler.cc
+++ b/src/jdwp/jdwp_handler.cc
@@ -277,7 +277,10 @@
  */
 static JdwpError VM_Suspend(JdwpState*, const uint8_t*, int, ExpandBuf*)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  Thread* self = Thread::Current();
+  self->TransitionFromRunnableToSuspended(kWaitingForDebuggerSuspension);
   Dbg::SuspendVM();
+  self->TransitionFromSuspendedToRunnable();
   return ERR_NONE;
 }
 
diff --git a/src/jdwp/jdwp_main.cc b/src/jdwp/jdwp_main.cc
index 0691515..33aadee 100644
--- a/src/jdwp/jdwp_main.cc
+++ b/src/jdwp/jdwp_main.cc
@@ -102,7 +102,7 @@
       serial_lock_("JDWP serial lock", kJdwpSerialLock),
       request_serial_(0x10000000),
       event_serial_(0x20000000),
-      event_list_lock_("JDWP event list lock"),
+      event_list_lock_("JDWP event list lock", kJdwpEventListLock),
       event_list_(NULL),
       event_list_size_(0),
       event_thread_lock_("JDWP event thread lock"),
diff --git a/src/locks.h b/src/locks.h
index c009f1d..9da7711 100644
--- a/src/locks.h
+++ b/src/locks.h
@@ -37,18 +37,19 @@
   kThreadSuspendCountLock = 2,
   kAbortLock = 3,
   kDefaultMutexLevel = 4,
-  kJdwpAttachLock = 5,
-  kJdwpStartLock = 6,
-  kJdwpSerialLock = 7,
-  kAllocSpaceLock = 8,
-  kLoadLibraryLock = 9,
-  kClassLinkerClassesLock = 10,
-  kThreadListLock = 11,
-  kRuntimeShutdownLock = 12,
-  kHeapBitmapLock = 13,
-  kMonitorLock = 14,
-  kMutatorLock = 15,
-  kZygoteCreationLock = 16,
+  kAllocSpaceLock = 5,
+  kLoadLibraryLock = 6,
+  kClassLinkerClassesLock = 7,
+  kThreadListLock = 8,
+  kJdwpEventListLock = 9,
+  kJdwpAttachLock = 10,
+  kJdwpStartLock = 11,
+  kJdwpSerialLock = 12,
+  kRuntimeShutdownLock = 13,
+  kHeapBitmapLock = 14,
+  kMonitorLock = 15,
+  kMutatorLock = 16,
+  kZygoteCreationLock = 17,
   kMaxMutexLevel = kZygoteCreationLock,
 };
 std::ostream& operator<<(std::ostream& os, const LockLevel& rhs);
diff --git a/src/thread.cc b/src/thread.cc
index 7490d2a..f6053a9 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -1725,8 +1725,6 @@
 
   void DoLongJump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     AbstractMethod* catch_method = *handler_quick_frame_;
-    Dbg::PostException(self_, throw_frame_id_, throw_method_, throw_dex_pc_,
-                       catch_method, handler_dex_pc_, exception_);
     if (kDebugExceptionDelivery) {
       if (catch_method == NULL) {
         LOG(INFO) << "Handler is upcall";
@@ -1738,6 +1736,9 @@
     }
     self_->SetException(exception_);  // Exception back in root set.
     self_->EndAssertNoThreadSuspension(last_no_assert_suspension_cause_);
+    // Do debugger PostException after allowing thread suspension again.
+    Dbg::PostException(self_, throw_frame_id_, throw_method_, throw_dex_pc_,
+                       catch_method, handler_dex_pc_, exception_);
     // Place context back on thread so it will be available when we continue.
     self_->ReleaseLongJumpContext(context_);
     context_->SetSP(reinterpret_cast<uintptr_t>(handler_quick_frame_));
diff --git a/src/thread.h b/src/thread.h
index 7bd64c8..4d97315 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -128,9 +128,11 @@
   }
 
   static Thread* FromManagedThread(const ScopedObjectAccessUnchecked& ts, Object* thread_peer)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_list_lock_)
       LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static Thread* FromManagedThread(const ScopedObjectAccessUnchecked& ts, jobject thread)
+      EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_list_lock_)
       LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
diff --git a/src/thread_list.cc b/src/thread_list.cc
index d39d424..3834725 100644
--- a/src/thread_list.cc
+++ b/src/thread_list.cc
@@ -384,16 +384,18 @@
   Thread* debug_thread = Dbg::GetDebugThread();
   CHECK(debug_thread != NULL);
   CHECK(self != debug_thread);
+  CHECK_NE(self->GetState(), kRunnable);
+  Locks::mutator_lock_->AssertNotHeld(self);
 
-  // Collisions with other suspends aren't really interesting. We want
-  // to ensure that we're the only one fiddling with the suspend count
-  // though.
-  MutexLock mu(self, *Locks::thread_suspend_count_lock_);
-  self->ModifySuspendCount(self, +1, true);
+  {
+    // Collisions with other suspends aren't really interesting. We want
+    // to ensure that we're the only one fiddling with the suspend count
+    // though.
+    MutexLock mu(self, *Locks::thread_suspend_count_lock_);
+    self->ModifySuspendCount(self, +1, true);
+    CHECK_GT(self->suspend_count_, 0);
+  }
 
-  // Suspend ourselves.
-  CHECK_GT(self->suspend_count_, 0);
-  self->SetState(kSuspended);
   VLOG(threads) << *self << " self-suspending (debugger)";
 
   // Tell JDWP that we've completed suspension. The JDWP thread can't
@@ -401,19 +403,22 @@
   // suspend count lock.
   Dbg::ClearWaitForEventThread();
 
-  while (self->suspend_count_ != 0) {
-    Thread::resume_cond_->Wait(self);
-    if (self->suspend_count_ != 0) {
-      // The condition was signaled but we're still suspended. This
-      // can happen if the debugger lets go while a SIGQUIT thread
-      // dump event is pending (assuming SignalCatcher was resumed for
-      // just long enough to try to grab the thread-suspend lock).
-      LOG(DEBUG) << *self << " still suspended after undo "
-                 << "(suspend count=" << self->suspend_count_ << ")";
+  {
+    MutexLock mu(self, *Locks::thread_suspend_count_lock_);
+    while (self->suspend_count_ != 0) {
+      Thread::resume_cond_->Wait(self);
+      if (self->suspend_count_ != 0) {
+        // The condition was signaled but we're still suspended. This
+        // can happen if the debugger lets go while a SIGQUIT thread
+        // dump event is pending (assuming SignalCatcher was resumed for
+        // just long enough to try to grab the thread-suspend lock).
+        LOG(DEBUG) << *self << " still suspended after undo "
+                   << "(suspend count=" << self->suspend_count_ << ")";
+      }
     }
+    CHECK_EQ(self->suspend_count_, 0);
   }
-  CHECK_EQ(self->suspend_count_, 0);
-  self->SetState(kRunnable);
+
   VLOG(threads) << *self << " self-reviving (debugger)";
 }
 
diff --git a/test/084-class-init/expected.txt b/test/084-class-init/expected.txt
index 5b0b3ff..1389214 100644
--- a/test/084-class-init/expected.txt
+++ b/test/084-class-init/expected.txt
@@ -2,6 +2,7 @@
 Got expected EIIE for FIELD0
 Got expected NCDFE for FIELD0
 Got expected NCDFE for FIELD1
+Got expected 'hello!' from Exploder
 SlowInit static block pre-sleep
 SlowInit static block post-sleep
 MethodThread message
diff --git a/test/084-class-init/src/Exploder.java b/test/084-class-init/src/Exploder.java
new file mode 100644
index 0000000..911e5fe
--- /dev/null
+++ b/test/084-class-init/src/Exploder.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+/**
+ * Throws an Error rather than an exception from its class initializer.
+ */
+public class Exploder {
+    public static final Object FIELD = new AssertThrower();
+    static class AssertThrower {
+        AssertThrower() {
+            throw new AssertionError("hello!");
+        }
+    }
+}
diff --git a/test/084-class-init/src/Main.java b/test/084-class-init/src/Main.java
index abad1f0..cf69570 100644
--- a/test/084-class-init/src/Main.java
+++ b/test/084-class-init/src/Main.java
@@ -67,6 +67,13 @@
         } catch (NoClassDefFoundError ncdfe) {
             System.out.println("Got expected NCDFE for FIELD1");
         }
+
+        try {
+            System.out.println(Exploder.FIELD);
+            System.err.println("Load of FIELD succeeded unexpectedly");
+        } catch (AssertionError expected) {
+            System.out.println("Got expected '" + expected.getMessage() + "' from Exploder");
+        }
     }
 
     static void checkTiming() {