Fast JNI support.

Use a modifier to signal a native method is a fast JNI method. If the
modifier is set then don't perform runnable transitions.

Change-Id: I7835b4d837bfdd1cb8e2d54b919c0d5e6cf90499
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index a653ab4..667b913 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -152,7 +152,7 @@
   std::string reason;
   ASSERT_TRUE(
       Runtime::Current()->GetJavaVM()->LoadNativeLibrary("", soa.Decode<mirror::ClassLoader*>(class_loader_),
-                                                         reason)) << reason;
+                                                         &reason)) << reason;
 
   jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 24);
   EXPECT_EQ(25, result);
@@ -167,7 +167,7 @@
   std::string reason;
   ASSERT_TRUE(
       Runtime::Current()->GetJavaVM()->LoadNativeLibrary("", soa.Decode<mirror::ClassLoader*>(class_loader_),
-                                                         reason)) << reason;
+                                                         &reason)) << reason;
 
   jint result = env_->CallStaticIntMethod(jklass_, jmethod_, 42);
   EXPECT_EQ(43, result);
diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc
index 83d3a58..16364fc 100644
--- a/runtime/entrypoints/jni/jni_entrypoints.cc
+++ b/runtime/entrypoints/jni/jni_entrypoints.cc
@@ -41,7 +41,7 @@
     return NULL;
   } else {
     // Register so that future calls don't come here
-    method->RegisterNative(self, native_code);
+    method->RegisterNative(self, native_code, false);
     return native_code;
   }
 }
@@ -115,7 +115,7 @@
   const void* code = reinterpret_cast<const void*>(jni_method->GetNativeGcMap());
   if (UNLIKELY(code == NULL)) {
     code = GetJniDlsymLookupStub();
-    jni_method->RegisterNative(self, code);
+    jni_method->RegisterNative(self, code, false);
   }
   return code;
 }
diff --git a/runtime/entrypoints/quick/quick_entrypoints.h b/runtime/entrypoints/quick/quick_entrypoints.h
index e9964ad..c8a85a0 100644
--- a/runtime/entrypoints/quick/quick_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_entrypoints.h
@@ -142,22 +142,23 @@
 
 
 // JNI entrypoints.
-extern uint32_t JniMethodStart(Thread* self) UNLOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR;
+// TODO: NO_THREAD_SAFETY_ANALYSIS due to different control paths depending on fast JNI.
+extern uint32_t JniMethodStart(Thread* self) NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
 extern uint32_t JniMethodStartSynchronized(jobject to_lock, Thread* self)
-    UNLOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR;
+    NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
 extern void JniMethodEnd(uint32_t saved_local_ref_cookie, Thread* self)
-    SHARED_LOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR;
+    NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
 extern void JniMethodEndSynchronized(uint32_t saved_local_ref_cookie, jobject locked,
                                      Thread* self)
-    SHARED_LOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR;
+    NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
 extern mirror::Object* JniMethodEndWithReference(jobject result, uint32_t saved_local_ref_cookie,
                                                  Thread* self)
-    SHARED_LOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR;
+    NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
 
 extern mirror::Object* JniMethodEndWithReferenceSynchronized(jobject result,
                                                              uint32_t saved_local_ref_cookie,
                                                              jobject locked, Thread* self)
-    SHARED_LOCK_FUNCTION(Locks::mutator_lock_) HOT_ATTR;
+    NO_THREAD_SAFETY_ANALYSIS HOT_ATTR;
 
 }  // namespace art
 
diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
index 27ae59b..59da7a0 100644
--- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
@@ -30,10 +30,14 @@
 // Called on entry to JNI, transition out of Runnable and release share of mutator_lock_.
 extern uint32_t JniMethodStart(Thread* self) {
   JNIEnvExt* env = self->GetJniEnv();
-  DCHECK(env != NULL);
+  DCHECK(env != nullptr);
   uint32_t saved_local_ref_cookie = env->local_ref_cookie;
   env->local_ref_cookie = env->locals.GetSegmentState();
-  self->TransitionFromRunnableToSuspended(kNative);
+  mirror::ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame();
+  if (!native_method->IsFastNative()) {
+    // When not fast JNI we transition out of runnable.
+    self->TransitionFromRunnableToSuspended(kNative);
+  }
   return saved_local_ref_cookie;
 }
 
@@ -42,6 +46,20 @@
   return JniMethodStart(self);
 }
 
+// TODO: NO_THREAD_SAFETY_ANALYSIS due to different control paths depending on fast JNI.
+static void GoToRunnable(Thread* self) NO_THREAD_SAFETY_ANALYSIS {
+  mirror::ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame();
+  bool is_fast = native_method->IsFastNative();
+  if (!is_fast) {
+    self->TransitionFromSuspendedToRunnable();
+  } else if (UNLIKELY(self->TestAllFlags())) {
+    // In fast JNI mode we never transitioned out of runnable. Perform a suspend check if there
+    // is a flag raised.
+    DCHECK(Locks::mutator_lock_->IsSharedHeld(self));
+    CheckSuspend(self);
+  }
+}
+
 static void PopLocalReferences(uint32_t saved_local_ref_cookie, Thread* self) {
   JNIEnvExt* env = self->GetJniEnv();
   env->locals.SetSegmentState(env->local_ref_cookie);
@@ -50,21 +68,21 @@
 }
 
 extern void JniMethodEnd(uint32_t saved_local_ref_cookie, Thread* self) {
-  self->TransitionFromSuspendedToRunnable();
+  GoToRunnable(self);
   PopLocalReferences(saved_local_ref_cookie, self);
 }
 
 
 extern void JniMethodEndSynchronized(uint32_t saved_local_ref_cookie, jobject locked,
                                      Thread* self) {
-  self->TransitionFromSuspendedToRunnable();
+  GoToRunnable(self);
   UnlockJniSynchronizedMethod(locked, self);  // Must decode before pop.
   PopLocalReferences(saved_local_ref_cookie, self);
 }
 
 extern mirror::Object* JniMethodEndWithReference(jobject result, uint32_t saved_local_ref_cookie,
                                                  Thread* self) {
-  self->TransitionFromSuspendedToRunnable();
+  GoToRunnable(self);
   mirror::Object* o = self->DecodeJObject(result);  // Must decode before pop.
   PopLocalReferences(saved_local_ref_cookie, self);
   // Process result.
@@ -80,7 +98,7 @@
 extern mirror::Object* JniMethodEndWithReferenceSynchronized(jobject result,
                                                              uint32_t saved_local_ref_cookie,
                                                              jobject locked, Thread* self) {
-  self->TransitionFromSuspendedToRunnable();
+  GoToRunnable(self);
   UnlockJniSynchronizedMethod(locked, self);  // Must decode before pop.
   mirror::Object* o = self->DecodeJObject(result);
   PopLocalReferences(saved_local_ref_cookie, self);
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index ed90242..d26e28c 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -2044,24 +2044,22 @@
   return concurrent_start_bytes_ != std::numeric_limits<size_t>::max();
 }
 
-void Heap::RegisterNativeAllocation(int bytes) {
+void Heap::RegisterNativeAllocation(JNIEnv* env, int bytes) {
   // Total number of native bytes allocated.
   native_bytes_allocated_.fetch_add(bytes);
-  Thread* self = Thread::Current();
   if (static_cast<size_t>(native_bytes_allocated_) > native_footprint_gc_watermark_) {
     // The second watermark is higher than the gc watermark. If you hit this it means you are
     // allocating native objects faster than the GC can keep up with.
     if (static_cast<size_t>(native_bytes_allocated_) > native_footprint_limit_) {
-        JNIEnv* env = self->GetJniEnv();
         // Can't do this in WellKnownClasses::Init since System is not properly set up at that
         // point.
-        if (WellKnownClasses::java_lang_System_runFinalization == NULL) {
+        if (UNLIKELY(WellKnownClasses::java_lang_System_runFinalization == NULL)) {
           DCHECK(WellKnownClasses::java_lang_System != NULL);
           WellKnownClasses::java_lang_System_runFinalization =
               CacheMethod(env, WellKnownClasses::java_lang_System, true, "runFinalization", "()V");
-          assert(WellKnownClasses::java_lang_System_runFinalization != NULL);
+          CHECK(WellKnownClasses::java_lang_System_runFinalization != NULL);
         }
-        if (WaitForConcurrentGcToComplete(self) != collector::kGcTypeNone) {
+        if (WaitForConcurrentGcToComplete(ThreadForEnv(env)) != collector::kGcTypeNone) {
           // Just finished a GC, attempt to run finalizers.
           env->CallStaticVoidMethod(WellKnownClasses::java_lang_System,
                                     WellKnownClasses::java_lang_System_runFinalization);
@@ -2080,20 +2078,22 @@
         UpdateMaxNativeFootprint();
     } else {
       if (!IsGCRequestPending()) {
-        RequestConcurrentGC(self);
+        RequestConcurrentGC(ThreadForEnv(env));
       }
     }
   }
 }
 
-void Heap::RegisterNativeFree(int bytes) {
+void Heap::RegisterNativeFree(JNIEnv* env, int bytes) {
   int expected_size, new_size;
   do {
       expected_size = native_bytes_allocated_.load();
       new_size = expected_size - bytes;
-      if (new_size < 0) {
-        ThrowRuntimeException("attempted to free %d native bytes with only %d native bytes registered as allocated",
-                              bytes, expected_size);
+      if (UNLIKELY(new_size < 0)) {
+        ScopedObjectAccess soa(env);
+        env->ThrowNew(WellKnownClasses::java_lang_RuntimeException,
+                      StringPrintf("Attempted to free %d native bytes with only %d native bytes "
+                                   "registered as allocated", bytes, expected_size).c_str());
         break;
       }
   } while (!native_bytes_allocated_.compare_and_swap(expected_size, new_size));
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index ffd3034..1c2b7ef 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -146,9 +146,8 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   void ThrowOutOfMemoryError(size_t byte_count, bool large_object_allocation);
 
-  void RegisterNativeAllocation(int bytes)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  void RegisterNativeFree(int bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  void RegisterNativeAllocation(JNIEnv* env, int bytes);
+  void RegisterNativeFree(JNIEnv* env, int bytes);
 
   // The given reference is believed to be to an object in the Java heap, check the soundness of it.
   void VerifyObjectImpl(const mirror::Object* o);
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 29fc7a4..ec717c1 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -2357,9 +2357,9 @@
     for (jint i = 0; i < method_count; ++i) {
       const char* name = methods[i].name;
       const char* sig = methods[i].signature;
-
+      bool is_fast = false;
       if (*sig == '!') {
-        // TODO: fast jni. it's too noisy to log all these.
+        is_fast = true;
         ++sig;
       }
 
@@ -2382,7 +2382,7 @@
 
       VLOG(jni) << "[Registering JNI native method " << PrettyMethod(m) << "]";
 
-      m->RegisterNative(soa.Self(), methods[i].fnPtr);
+      m->RegisterNative(soa.Self(), methods[i].fnPtr, is_fast);
     }
     return JNI_OK;
   }
@@ -3107,8 +3107,8 @@
 }
 
 bool JavaVMExt::LoadNativeLibrary(const std::string& path, ClassLoader* class_loader,
-                                  std::string& detail) {
-  detail.clear();
+                                  std::string* detail) {
+  detail->clear();
 
   // See if we've already loaded this library.  If we have, and the class loader
   // matches, return successfully without doing anything.
@@ -3126,7 +3126,7 @@
       // The library will be associated with class_loader. The JNI
       // spec says we can't load the same library into more than one
       // class loader.
-      StringAppendF(&detail, "Shared library \"%s\" already opened by "
+      StringAppendF(detail, "Shared library \"%s\" already opened by "
           "ClassLoader %p; can't open in ClassLoader %p",
           path.c_str(), library->GetClassLoader(), class_loader);
       LOG(WARNING) << detail;
@@ -3135,7 +3135,7 @@
     VLOG(jni) << "[Shared library \"" << path << "\" already loaded in "
               << "ClassLoader " << class_loader << "]";
     if (!library->CheckOnLoadResult()) {
-      StringAppendF(&detail, "JNI_OnLoad failed on a previous attempt "
+      StringAppendF(detail, "JNI_OnLoad failed on a previous attempt "
           "to load \"%s\"", path.c_str());
       return false;
     }
@@ -3162,7 +3162,7 @@
   VLOG(jni) << "[Call to dlopen(\"" << path << "\", RTLD_LAZY) returned " << handle << "]";
 
   if (handle == NULL) {
-    detail = dlerror();
+    *detail = dlerror();
     LOG(ERROR) << "dlopen(\"" << path << "\", RTLD_LAZY) failed: " << detail;
     return false;
   }
@@ -3212,9 +3212,9 @@
     self->SetClassLoaderOverride(old_class_loader);
 
     if (version == JNI_ERR) {
-      StringAppendF(&detail, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
+      StringAppendF(detail, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
     } else if (IsBadJniVersion(version)) {
-      StringAppendF(&detail, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
+      StringAppendF(detail, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
                     path.c_str(), version);
       // It's unwise to call dlclose() here, but we can mark it
       // as bad and ensure that future load attempts will fail.
diff --git a/runtime/jni_internal.h b/runtime/jni_internal.h
index c73ed48..888d5e5 100644
--- a/runtime/jni_internal.h
+++ b/runtime/jni_internal.h
@@ -73,7 +73,7 @@
    * human-readable description of the error.
    */
   bool LoadNativeLibrary(const std::string& path, mirror::ClassLoader* class_loader,
-                         std::string& detail)
+                         std::string* detail)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   /**
diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc
index cd05f41..f5c0e9f 100644
--- a/runtime/mirror/art_method.cc
+++ b/runtime/mirror/art_method.cc
@@ -306,11 +306,15 @@
 }
 
 extern "C" void art_work_around_app_jni_bugs(JNIEnv*, jobject);
-void ArtMethod::RegisterNative(Thread* self, const void* native_method) {
+void ArtMethod::RegisterNative(Thread* self, const void* native_method, bool is_fast) {
   DCHECK(Thread::Current() == self);
   CHECK(IsNative()) << PrettyMethod(this);
+  CHECK(!IsFastNative()) << PrettyMethod(this);
   CHECK(native_method != NULL) << PrettyMethod(this);
   if (!self->GetJniEnv()->vm->work_around_app_jni_bugs) {
+    if (is_fast) {
+      SetAccessFlags(GetAccessFlags() | kAccFastNative);
+    }
     SetNativeMethod(native_method);
   } else {
     // We've been asked to associate this method with the given native method but are working
@@ -328,9 +332,9 @@
 }
 
 void ArtMethod::UnregisterNative(Thread* self) {
-  CHECK(IsNative()) << PrettyMethod(this);
+  CHECK(IsNative() && !IsFastNative()) << PrettyMethod(this);
   // restore stub to lookup native pointer via dlsym
-  RegisterNative(self, GetJniDlsymLookupStub());
+  RegisterNative(self, GetJniDlsymLookupStub(), false);
 }
 
 void ArtMethod::SetNativeMethod(const void* native_method) {
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index 5d4a6ea..0520893 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -112,6 +112,10 @@
     return (GetAccessFlags() & kAccNative) != 0;
   }
 
+  bool IsFastNative() const {
+    return (GetAccessFlags() & kAccFastNative) != 0;
+  }
+
   bool IsAbstract() const {
     return (GetAccessFlags() & kAccAbstract) != 0;
   }
@@ -307,7 +311,7 @@
 
   bool IsRegistered() const;
 
-  void RegisterNative(Thread* self, const void* native_method)
+  void RegisterNative(Thread* self, const void* native_method, bool is_fast)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   void UnregisterNative(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 2b0b1e1..319ca4a 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -661,7 +661,9 @@
     for (int32_t index = 0, end = methods->GetLength(); index < end; ++index) {
       mirror::ArtMethod* method = methods->GetWithoutChecks(index);
       DCHECK(method != NULL);
-      method->SetPreverified();
+      if (!method->IsNative() && !method->IsAbstract()) {
+        method->SetPreverified();
+      }
     }
   }
 }
diff --git a/runtime/modifiers.h b/runtime/modifiers.h
index 34f4af8..4e365be 100644
--- a/runtime/modifiers.h
+++ b/runtime/modifiers.h
@@ -46,6 +46,7 @@
 static const uint32_t kAccDeclaredSynchronized = 0x00020000;  // method (dex only)
 static const uint32_t kAccClassIsProxy = 0x00040000;  // class (dex only)
 static const uint32_t kAccPreverified = 0x00080000;  // method (dex only)
+static const uint32_t kAccFastNative = 0x0080000;  // method (dex only)
 
 // Special runtime-only flags.
 // Note: if only kAccClassIsReference is set, we have a soft reference.
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 823013a..4e17b79 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -95,7 +95,6 @@
     return 0;
   }
   ScopedObjectAccess soa(env);
-
   uint32_t dex_location_checksum;
   if (!DexFile::GetChecksum(dex_location, &dex_location_checksum)) {
     LOG(WARNING) << "Failed to compute checksum: " << dex_location;
@@ -123,9 +122,10 @@
   return static_cast<jint>(reinterpret_cast<uintptr_t>(dex_file));
 }
 
-static const DexFile* toDexFile(int dex_file_address) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+static const DexFile* toDexFile(int dex_file_address, JNIEnv* env) {
   const DexFile* dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(dex_file_address));
-  if (dex_file == NULL) {
+  if (UNLIKELY(dex_file == nullptr)) {
+    ScopedObjectAccess soa(env);
     ThrowNullPointerException(NULL, "dex_file == null");
   }
   return dex_file;
@@ -133,11 +133,8 @@
 
 static void DexFile_closeDexFile(JNIEnv* env, jclass, jint cookie) {
   const DexFile* dex_file;
-  {
-    ScopedObjectAccess soa(env);
-    dex_file = toDexFile(cookie);
-  }
-  if (dex_file == NULL) {
+  dex_file = toDexFile(cookie, env);
+  if (dex_file == nullptr) {
     return;
   }
   if (Runtime::Current()->GetClassLinker()->IsDexFileRegistered(*dex_file)) {
@@ -148,8 +145,7 @@
 
 static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader,
                                         jint cookie) {
-  ScopedObjectAccess soa(env);
-  const DexFile* dex_file = toDexFile(cookie);
+  const DexFile* dex_file = toDexFile(cookie, env);
   if (dex_file == NULL) {
     VLOG(class_linker) << "Failed to find dex_file";
     return NULL;
@@ -165,6 +161,7 @@
     VLOG(class_linker) << "Failed to find dex_class_def";
     return NULL;
   }
+  ScopedObjectAccess soa(env);
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   class_linker->RegisterDexFile(*dex_file);
   mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(javaLoader);
@@ -176,12 +173,9 @@
 
 static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jint cookie) {
   const DexFile* dex_file;
-  {
-    ScopedObjectAccess soa(env);
-    dex_file = toDexFile(cookie);
-  }
-  if (dex_file == NULL) {
-    return NULL;
+  dex_file = toDexFile(cookie, env);
+  if (dex_file == nullptr) {
+    return nullptr;
   }
 
   std::vector<std::string> class_names;
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index dad6eff..9ad2585 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -28,6 +28,7 @@
 #include "mirror/object.h"
 #include "mirror/object-inl.h"
 #include "object_utils.h"
+#include "scoped_fast_native_object_access.h"
 #include "scoped_thread_state_change.h"
 #include "thread.h"
 #include "thread_list.h"
@@ -50,7 +51,7 @@
 }
 
 static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaElementClass, jint length) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
 #ifdef MOVING_GARBAGE_COLLECTOR
   // TODO: right now, we don't have a copying collector, so there's no need
   // to do anything special here, but we ought to pass the non-movability
@@ -81,7 +82,7 @@
   if (javaArray == NULL) {  // Most likely allocation failed
     return 0;
   }
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Array* array = soa.Decode<mirror::Array*>(javaArray);
   if (!array->IsArrayInstance()) {
     ThrowIllegalArgumentException(NULL, "not an array");
@@ -147,21 +148,21 @@
 }
 
 static void VMRuntime_registerNativeAllocation(JNIEnv* env, jobject, jint bytes) {
-  ScopedObjectAccess soa(env);
-  if (bytes < 0) {
+  if (UNLIKELY(bytes < 0)) {
+    ScopedObjectAccess soa(env);
     ThrowRuntimeException("allocation size negative %d", bytes);
     return;
   }
-  Runtime::Current()->GetHeap()->RegisterNativeAllocation(bytes);
+  Runtime::Current()->GetHeap()->RegisterNativeAllocation(env, bytes);
 }
 
 static void VMRuntime_registerNativeFree(JNIEnv* env, jobject, jint bytes) {
-  ScopedObjectAccess soa(env);
-  if (bytes < 0) {
+  if (UNLIKELY(bytes < 0)) {
+    ScopedObjectAccess soa(env);
     ThrowRuntimeException("allocation size negative %d", bytes);
     return;
   }
-  Runtime::Current()->GetHeap()->RegisterNativeFree(bytes);
+  Runtime::Current()->GetHeap()->RegisterNativeFree(env, bytes);
 }
 
 static void VMRuntime_trimHeap(JNIEnv*, jobject) {
@@ -189,12 +190,12 @@
 }
 
 static void VMRuntime_concurrentGC(JNIEnv* env, jobject) {
-  Thread* self = static_cast<JNIEnvExt*>(env)->self;
+  Thread* self = ThreadForEnv(env);
   Runtime::Current()->GetHeap()->ConcurrentGC(self);
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(VMRuntime, addressOf, "(Ljava/lang/Object;)J"),
+  NATIVE_METHOD(VMRuntime, addressOf, "!(Ljava/lang/Object;)J"),
   NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"),
   NATIVE_METHOD(VMRuntime, classPath, "()Ljava/lang/String;"),
   NATIVE_METHOD(VMRuntime, clearGrowthLimit, "()V"),
@@ -203,7 +204,7 @@
   NATIVE_METHOD(VMRuntime, getTargetHeapUtilization, "()F"),
   NATIVE_METHOD(VMRuntime, isDebuggerActive, "()Z"),
   NATIVE_METHOD(VMRuntime, nativeSetTargetHeapUtilization, "(F)V"),
-  NATIVE_METHOD(VMRuntime, newNonMovableArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"),
+  NATIVE_METHOD(VMRuntime, newNonMovableArray, "!(Ljava/lang/Class;I)Ljava/lang/Object;"),
   NATIVE_METHOD(VMRuntime, properties, "()[Ljava/lang/String;"),
   NATIVE_METHOD(VMRuntime, setTargetSdkVersion, "(I)V"),
   NATIVE_METHOD(VMRuntime, registerNativeAllocation, "(I)V"),
diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc
index 5508270..f915365 100644
--- a/runtime/native/dalvik_system_VMStack.cc
+++ b/runtime/native/dalvik_system_VMStack.cc
@@ -20,6 +20,7 @@
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
 #include "mirror/object-inl.h"
+#include "scoped_fast_native_object_access.h"
 #include "scoped_thread_state_change.h"
 #include "thread_list.h"
 
@@ -66,7 +67,7 @@
 
 // Returns the defining class loader of the caller's caller.
 static jobject VMStack_getCallingClassLoader(JNIEnv* env, jclass) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   NthCallerVisitor visitor(soa.Self(), 2);
   visitor.WalkStack();
   return soa.AddLocalReference<jobject>(visitor.caller->GetDeclaringClass()->GetClassLoader());
@@ -93,7 +94,7 @@
     mirror::Object* system;
     mirror::Object* class_loader;
   };
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* bootstrap = soa.Decode<mirror::Object*>(javaBootstrap);
   mirror::Object* system = soa.Decode<mirror::Object*>(javaSystem);
   ClosestUserClassLoaderVisitor visitor(soa.Self(), bootstrap, system);
@@ -103,7 +104,7 @@
 
 // Returns the class of the caller's caller's caller.
 static jclass VMStack_getStackClass2(JNIEnv* env, jclass) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   NthCallerVisitor visitor(soa.Self(), 3);
   visitor.WalkStack();
   return soa.AddLocalReference<jclass>(visitor.caller->GetDeclaringClass());
@@ -119,9 +120,9 @@
 
 static JNINativeMethod gMethods[] = {
   NATIVE_METHOD(VMStack, fillStackTraceElements, "(Ljava/lang/Thread;[Ljava/lang/StackTraceElement;)I"),
-  NATIVE_METHOD(VMStack, getCallingClassLoader, "()Ljava/lang/ClassLoader;"),
-  NATIVE_METHOD(VMStack, getClosestUserClassLoader, "(Ljava/lang/ClassLoader;Ljava/lang/ClassLoader;)Ljava/lang/ClassLoader;"),
-  NATIVE_METHOD(VMStack, getStackClass2, "()Ljava/lang/Class;"),
+  NATIVE_METHOD(VMStack, getCallingClassLoader, "!()Ljava/lang/ClassLoader;"),
+  NATIVE_METHOD(VMStack, getClosestUserClassLoader, "!(Ljava/lang/ClassLoader;Ljava/lang/ClassLoader;)Ljava/lang/ClassLoader;"),
+  NATIVE_METHOD(VMStack, getStackClass2, "!()Ljava/lang/Class;"),
   NATIVE_METHOD(VMStack, getThreadStackTrace, "(Ljava/lang/Thread;)[Ljava/lang/StackTraceElement;"),
 };
 
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index d3011cb..3591611 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -24,13 +24,14 @@
 #include "mirror/proxy.h"
 #include "object_utils.h"
 #include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
 #include "ScopedLocalRef.h"
 #include "ScopedUtfChars.h"
 #include "well_known_classes.h"
 
 namespace art {
 
-static mirror::Class* DecodeClass(const ScopedObjectAccess& soa, jobject java_class)
+static mirror::Class* DecodeClass(const ScopedFastNativeObjectAccess& soa, jobject java_class)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   mirror::Class* c = soa.Decode<mirror::Class*>(java_class);
   DCHECK(c != NULL);
@@ -79,13 +80,13 @@
 }
 
 static jstring Class_getNameNative(JNIEnv* env, jobject javaThis) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Class* c = DecodeClass(soa, javaThis);
   return soa.AddLocalReference<jstring>(c->ComputeName());
 }
 
 static jobjectArray Class_getProxyInterfaces(JNIEnv* env, jobject javaThis) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::SynthesizedProxyClass* c =
       down_cast<mirror::SynthesizedProxyClass*>(DecodeClass(soa, javaThis));
   return soa.AddLocalReference<jobjectArray>(c->GetInterfaces()->Clone(soa.Self()));
@@ -93,8 +94,8 @@
 
 static JNINativeMethod gMethods[] = {
   NATIVE_METHOD(Class, classForName, "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"),
-  NATIVE_METHOD(Class, getNameNative, "()Ljava/lang/String;"),
-  NATIVE_METHOD(Class, getProxyInterfaces, "()[Ljava/lang/Class;"),
+  NATIVE_METHOD(Class, getNameNative, "!()Ljava/lang/String;"),
+  NATIVE_METHOD(Class, getProxyInterfaces, "!()[Ljava/lang/Class;"),
 };
 
 void register_java_lang_Class(JNIEnv* env) {
diff --git a/runtime/native/java_lang_DexCache.cc b/runtime/native/java_lang_DexCache.cc
index 9b83206..51cd5b8 100644
--- a/runtime/native/java_lang_DexCache.cc
+++ b/runtime/native/java_lang_DexCache.cc
@@ -17,13 +17,13 @@
 #include "dex_file.h"
 #include "mirror/dex_cache.h"
 #include "mirror/object-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
 #include "well_known_classes.h"
 
 namespace art {
 
 static jobject DexCache_getDexNative(JNIEnv* env, jobject javaDexCache) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::DexCache* dex_cache = soa.Decode<mirror::DexCache*>(javaDexCache);
   // Should only be called while holding the lock on the dex cache.
   DCHECK_EQ(dex_cache->GetLockOwnerThreadId(), soa.Self()->GetThreadId());
@@ -46,7 +46,7 @@
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(DexCache, getDexNative, "()Lcom/android/dex/Dex;"),
+  NATIVE_METHOD(DexCache, getDexNative, "!()Lcom/android/dex/Dex;"),
 };
 
 void register_java_lang_DexCache(JNIEnv* env) {
diff --git a/runtime/native/java_lang_Object.cc b/runtime/native/java_lang_Object.cc
index 5db7a33..4768f48 100644
--- a/runtime/native/java_lang_Object.cc
+++ b/runtime/native/java_lang_Object.cc
@@ -16,7 +16,7 @@
 
 #include "jni_internal.h"
 #include "mirror/object-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
 
 // TODO: better support for overloading.
 #undef NATIVE_METHOD
@@ -26,41 +26,41 @@
 namespace art {
 
 static jobject Object_internalClone(JNIEnv* env, jobject java_this) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* o = soa.Decode<mirror::Object*>(java_this);
   return soa.AddLocalReference<jobject>(o->Clone(soa.Self()));
 }
 
 static void Object_notify(JNIEnv* env, jobject java_this) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* o = soa.Decode<mirror::Object*>(java_this);
   o->Notify(soa.Self());
 }
 
 static void Object_notifyAll(JNIEnv* env, jobject java_this) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* o = soa.Decode<mirror::Object*>(java_this);
   o->NotifyAll(soa.Self());
 }
 
 static void Object_wait(JNIEnv* env, jobject java_this) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* o = soa.Decode<mirror::Object*>(java_this);
   o->Wait(soa.Self());
 }
 
 static void Object_waitJI(JNIEnv* env, jobject java_this, jlong ms, jint ns) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* o = soa.Decode<mirror::Object*>(java_this);
   o->Wait(soa.Self(), ms, ns);
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(Object, internalClone, "()Ljava/lang/Object;", internalClone),
-  NATIVE_METHOD(Object, notify, "()V", notify),
-  NATIVE_METHOD(Object, notifyAll, "()V", notifyAll),
-  NATIVE_METHOD(Object, wait, "()V", wait),
-  NATIVE_METHOD(Object, wait, "(JI)V", waitJI),
+  NATIVE_METHOD(Object, internalClone, "!()Ljava/lang/Object;", internalClone),
+  NATIVE_METHOD(Object, notify, "!()V", notify),
+  NATIVE_METHOD(Object, notifyAll, "!()V", notifyAll),
+  NATIVE_METHOD(Object, wait, "!()V", wait),
+  NATIVE_METHOD(Object, wait, "!(JI)V", waitJI),
 };
 
 void register_java_lang_Object(JNIEnv* env) {
diff --git a/runtime/native/java_lang_Runtime.cc b/runtime/native/java_lang_Runtime.cc
index 55575cf..e969fcf 100644
--- a/runtime/native/java_lang_Runtime.cc
+++ b/runtime/native/java_lang_Runtime.cc
@@ -41,7 +41,6 @@
 }
 
 static jstring Runtime_nativeLoad(JNIEnv* env, jclass, jstring javaFilename, jobject javaLoader, jstring javaLdLibraryPath) {
-  ScopedObjectAccess soa(env);
   ScopedUtfChars filename(env, javaFilename);
   if (filename.c_str() == NULL) {
     return NULL;
@@ -62,12 +61,15 @@
     }
   }
 
-  mirror::ClassLoader* classLoader = soa.Decode<mirror::ClassLoader*>(javaLoader);
   std::string detail;
-  JavaVMExt* vm = Runtime::Current()->GetJavaVM();
-  bool success = vm->LoadNativeLibrary(filename.c_str(), classLoader, detail);
-  if (success) {
-    return NULL;
+  {
+    ScopedObjectAccess soa(env);
+    mirror::ClassLoader* classLoader = soa.Decode<mirror::ClassLoader*>(javaLoader);
+    JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+    bool success = vm->LoadNativeLibrary(filename.c_str(), classLoader, &detail);
+    if (success) {
+      return nullptr;
+    }
   }
 
   // Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF.
diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc
index 3e9c3f3..c401d50 100644
--- a/runtime/native/java_lang_String.cc
+++ b/runtime/native/java_lang_String.cc
@@ -17,13 +17,14 @@
 #include "common_throws.h"
 #include "jni_internal.h"
 #include "mirror/string.h"
+#include "scoped_fast_native_object_access.h"
 #include "scoped_thread_state_change.h"
 #include "ScopedLocalRef.h"
 
 namespace art {
 
 static jint String_compareTo(JNIEnv* env, jobject javaThis, jobject javaRhs) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   if (UNLIKELY(javaRhs == NULL)) {
     ThrowNullPointerException(NULL, "rhs == null");
     return -1;
@@ -33,7 +34,7 @@
 }
 
 static jint String_fastIndexOf(JNIEnv* env, jobject java_this, jint ch, jint start) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   // This method does not handle supplementary characters. They're dealt with in managed code.
   DCHECK_LE(ch, 0xffff);
 
@@ -42,16 +43,16 @@
 }
 
 static jstring String_intern(JNIEnv* env, jobject javaThis) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::String* s = soa.Decode<mirror::String*>(javaThis);
   mirror::String* result = s->Intern();
   return soa.AddLocalReference<jstring>(result);
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(String, compareTo, "(Ljava/lang/String;)I"),
-  NATIVE_METHOD(String, fastIndexOf, "(II)I"),
-  NATIVE_METHOD(String, intern, "()Ljava/lang/String;"),
+  NATIVE_METHOD(String, compareTo, "!(Ljava/lang/String;)I"),
+  NATIVE_METHOD(String, fastIndexOf, "!(II)I"),
+  NATIVE_METHOD(String, intern, "!()Ljava/lang/String;"),
 };
 
 void register_java_lang_String(JNIEnv* env) {
diff --git a/runtime/native/java_lang_System.cc b/runtime/native/java_lang_System.cc
index 100f5a9..6674db2 100644
--- a/runtime/native/java_lang_System.cc
+++ b/runtime/native/java_lang_System.cc
@@ -22,7 +22,7 @@
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
 
 /*
  * We make guarantees about the atomicity of accesses to primitive
@@ -179,7 +179,7 @@
 }
 
 static void System_arraycopy(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, jobject javaDst, jint dstPos, jint length) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
 
   // Null pointer checks.
   if (UNLIKELY(javaSrc == NULL)) {
@@ -317,7 +317,7 @@
 }
 
 static void System_arraycopyCharUnchecked(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, jobject javaDst, jint dstPos, jint length) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   DCHECK(javaSrc != NULL);
   DCHECK(javaDst != NULL);
   mirror::Object* srcObject = soa.Decode<mirror::Object*>(javaSrc);
@@ -339,15 +339,15 @@
 }
 
 static jint System_identityHashCode(JNIEnv* env, jclass, jobject javaObject) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* o = soa.Decode<mirror::Object*>(javaObject);
   return static_cast<jint>(o->IdentityHashCode());
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(System, arraycopy, "(Ljava/lang/Object;ILjava/lang/Object;II)V"),
-  NATIVE_METHOD(System, arraycopyCharUnchecked, "([CI[CII)V"),
-  NATIVE_METHOD(System, identityHashCode, "(Ljava/lang/Object;)I"),
+  NATIVE_METHOD(System, arraycopy, "!(Ljava/lang/Object;ILjava/lang/Object;II)V"),
+  NATIVE_METHOD(System, arraycopyCharUnchecked, "!([CI[CII)V"),
+  NATIVE_METHOD(System, identityHashCode, "!(Ljava/lang/Object;)I"),
 };
 
 void register_java_lang_System(JNIEnv* env) {
diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc
index a9de086..5b34cfb 100644
--- a/runtime/native/java_lang_Thread.cc
+++ b/runtime/native/java_lang_Thread.cc
@@ -19,6 +19,7 @@
 #include "jni_internal.h"
 #include "monitor.h"
 #include "mirror/object.h"
+#include "scoped_fast_native_object_access.h"
 #include "scoped_thread_state_change.h"
 #include "ScopedUtfChars.h"
 #include "thread.h"
@@ -27,7 +28,7 @@
 namespace art {
 
 static jobject Thread_currentThread(JNIEnv* env, jclass) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   return soa.AddLocalReference<jobject>(soa.Self()->GetPeer());
 }
 
@@ -150,7 +151,7 @@
 }
 
 static void Thread_sleep(JNIEnv* env, jclass, jobject java_lock, jlong ms, jint ns) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* lock = soa.Decode<mirror::Object*>(java_lock);
   Monitor::Wait(Thread::Current(), lock, ms, ns, true, kSleeping);
 }
@@ -166,7 +167,7 @@
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(Thread, currentThread, "()Ljava/lang/Thread;"),
+  NATIVE_METHOD(Thread, currentThread, "!()Ljava/lang/Thread;"),
   NATIVE_METHOD(Thread, interrupted, "()Z"),
   NATIVE_METHOD(Thread, isInterrupted, "()Z"),
   NATIVE_METHOD(Thread, nativeCreate, "(Ljava/lang/Thread;JZ)V"),
@@ -175,7 +176,7 @@
   NATIVE_METHOD(Thread, nativeInterrupt, "()V"),
   NATIVE_METHOD(Thread, nativeSetName, "(Ljava/lang/String;)V"),
   NATIVE_METHOD(Thread, nativeSetPriority, "(I)V"),
-  NATIVE_METHOD(Thread, sleep, "(Ljava/lang/Object;JI)V"),
+  NATIVE_METHOD(Thread, sleep, "!(Ljava/lang/Object;JI)V"),
   NATIVE_METHOD(Thread, yield, "()V"),
 };
 
diff --git a/runtime/native/java_lang_reflect_Array.cc b/runtime/native/java_lang_reflect_Array.cc
index 45ec0ad..a2d6b18 100644
--- a/runtime/native/java_lang_reflect_Array.cc
+++ b/runtime/native/java_lang_reflect_Array.cc
@@ -21,13 +21,13 @@
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
 #include "object_utils.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
 #include "sirt_ref.h"
 
 namespace art {
 
 static jobject Array_createMultiArray(JNIEnv* env, jclass, jclass javaElementClass, jobject javaDimArray) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   DCHECK(javaElementClass != NULL);
   mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass);
   DCHECK(element_class->IsClass());
@@ -41,7 +41,7 @@
 }
 
 static jobject Array_createObjectArray(JNIEnv* env, jclass, jclass javaElementClass, jint length) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   DCHECK(javaElementClass != NULL);
   mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass);
   if (UNLIKELY(length < 0)) {
@@ -63,8 +63,8 @@
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(Array, createMultiArray, "(Ljava/lang/Class;[I)Ljava/lang/Object;"),
-  NATIVE_METHOD(Array, createObjectArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"),
+  NATIVE_METHOD(Array, createMultiArray, "!(Ljava/lang/Class;[I)Ljava/lang/Object;"),
+  NATIVE_METHOD(Array, createObjectArray, "!(Ljava/lang/Class;I)Ljava/lang/Object;"),
 };
 
 void register_java_lang_reflect_Array(JNIEnv* env) {
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc
index 85556ac..aa72755 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -35,6 +35,7 @@
  * with an interface, array, or primitive class.
  */
 static jobject Constructor_newInstance(JNIEnv* env, jobject javaMethod, jobjectArray javaArgs) {
+  // TODO: ScopedFastNativeObjectAccess
   ScopedObjectAccess soa(env);
   jobject art_method = soa.Env()->GetObjectField(
       javaMethod, WellKnownClasses::java_lang_reflect_AbstractMethod_artMethod);
@@ -68,7 +69,7 @@
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(Constructor, newInstance, "([Ljava/lang/Object;)Ljava/lang/Object;"),
+  NATIVE_METHOD(Constructor, newInstance, "!([Ljava/lang/Object;)Ljava/lang/Object;"),
 };
 
 void register_java_lang_reflect_Constructor(JNIEnv* env) {
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 00f89b6..4d69a68 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -23,12 +23,12 @@
 #include "mirror/class-inl.h"
 #include "object_utils.h"
 #include "reflection.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
 
 namespace art {
 
-static bool GetFieldValue(const ScopedObjectAccess& soa, mirror::Object* o, mirror::ArtField* f,
-                          JValue& value, bool allow_references)
+static bool GetFieldValue(const ScopedFastNativeObjectAccess& soa, mirror::Object* o,
+                          mirror::ArtField* f, JValue& value, bool allow_references)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   DCHECK_EQ(value.GetJ(), 0LL);
   if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(f->GetDeclaringClass(),
@@ -77,8 +77,8 @@
   return false;
 }
 
-static bool CheckReceiver(const ScopedObjectAccess& soa, jobject j_rcvr, mirror::ArtField* f,
-                          mirror::Object*& class_or_rcvr)
+static bool CheckReceiver(const ScopedFastNativeObjectAccess& soa, jobject j_rcvr,
+                          mirror::ArtField* f, mirror::Object*& class_or_rcvr)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   if (f->IsStatic()) {
     class_or_rcvr = f->GetDeclaringClass();
@@ -94,7 +94,7 @@
 }
 
 static jobject Field_get(JNIEnv* env, jobject javaField, jobject javaObj) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::ArtField* f = soa.DecodeField(env->FromReflectedField(javaField));
   mirror::Object* o = NULL;
   if (!CheckReceiver(soa, javaObj, f, o)) {
@@ -112,7 +112,7 @@
 
 static JValue GetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj,
                                 char dst_descriptor) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::ArtField* f = soa.DecodeField(env->FromReflectedField(javaField));
   mirror::Object* o = NULL;
   if (!CheckReceiver(soa, javaObj, f, o)) {
@@ -221,7 +221,7 @@
 }
 
 static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject javaValue) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::ArtField* f = soa.DecodeField(env->FromReflectedField(javaField));
 
   // Unbox the value, if necessary.
@@ -242,7 +242,7 @@
 
 static void SetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, char src_descriptor,
                               const JValue& new_value) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::ArtField* f = soa.DecodeField(env->FromReflectedField(javaField));
   mirror::Object* o = NULL;
   if (!CheckReceiver(soa, javaObj, f, o)) {
@@ -316,24 +316,24 @@
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(Field, get,        "(Ljava/lang/Object;)Ljava/lang/Object;"),
-  NATIVE_METHOD(Field, getBoolean, "(Ljava/lang/Object;)Z"),
-  NATIVE_METHOD(Field, getByte,    "(Ljava/lang/Object;)B"),
-  NATIVE_METHOD(Field, getChar,    "(Ljava/lang/Object;)C"),
-  NATIVE_METHOD(Field, getDouble,  "(Ljava/lang/Object;)D"),
-  NATIVE_METHOD(Field, getFloat,   "(Ljava/lang/Object;)F"),
-  NATIVE_METHOD(Field, getInt,     "(Ljava/lang/Object;)I"),
-  NATIVE_METHOD(Field, getLong,    "(Ljava/lang/Object;)J"),
-  NATIVE_METHOD(Field, getShort,   "(Ljava/lang/Object;)S"),
-  NATIVE_METHOD(Field, set,        "(Ljava/lang/Object;Ljava/lang/Object;)V"),
-  NATIVE_METHOD(Field, setBoolean, "(Ljava/lang/Object;Z)V"),
-  NATIVE_METHOD(Field, setByte,    "(Ljava/lang/Object;B)V"),
-  NATIVE_METHOD(Field, setChar,    "(Ljava/lang/Object;C)V"),
-  NATIVE_METHOD(Field, setDouble,  "(Ljava/lang/Object;D)V"),
-  NATIVE_METHOD(Field, setFloat,   "(Ljava/lang/Object;F)V"),
-  NATIVE_METHOD(Field, setInt,     "(Ljava/lang/Object;I)V"),
-  NATIVE_METHOD(Field, setLong,    "(Ljava/lang/Object;J)V"),
-  NATIVE_METHOD(Field, setShort,   "(Ljava/lang/Object;S)V"),
+  NATIVE_METHOD(Field, get,        "!(Ljava/lang/Object;)Ljava/lang/Object;"),
+  NATIVE_METHOD(Field, getBoolean, "!(Ljava/lang/Object;)Z"),
+  NATIVE_METHOD(Field, getByte,    "!(Ljava/lang/Object;)B"),
+  NATIVE_METHOD(Field, getChar,    "!(Ljava/lang/Object;)C"),
+  NATIVE_METHOD(Field, getDouble,  "!(Ljava/lang/Object;)D"),
+  NATIVE_METHOD(Field, getFloat,   "!(Ljava/lang/Object;)F"),
+  NATIVE_METHOD(Field, getInt,     "!(Ljava/lang/Object;)I"),
+  NATIVE_METHOD(Field, getLong,    "!(Ljava/lang/Object;)J"),
+  NATIVE_METHOD(Field, getShort,   "!(Ljava/lang/Object;)S"),
+  NATIVE_METHOD(Field, set,        "!(Ljava/lang/Object;Ljava/lang/Object;)V"),
+  NATIVE_METHOD(Field, setBoolean, "!(Ljava/lang/Object;Z)V"),
+  NATIVE_METHOD(Field, setByte,    "!(Ljava/lang/Object;B)V"),
+  NATIVE_METHOD(Field, setChar,    "!(Ljava/lang/Object;C)V"),
+  NATIVE_METHOD(Field, setDouble,  "!(Ljava/lang/Object;D)V"),
+  NATIVE_METHOD(Field, setFloat,   "!(Ljava/lang/Object;F)V"),
+  NATIVE_METHOD(Field, setInt,     "!(Ljava/lang/Object;I)V"),
+  NATIVE_METHOD(Field, setLong,    "!(Ljava/lang/Object;J)V"),
+  NATIVE_METHOD(Field, setShort,   "!(Ljava/lang/Object;S)V"),
 };
 
 void register_java_lang_reflect_Field(JNIEnv* env) {
diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
index d7cd18d..163ae20 100644
--- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
+++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
@@ -16,21 +16,21 @@
 
 #include "base/logging.h"
 #include "debugger.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
 #include "ScopedPrimitiveArray.h"
 
 namespace art {
 
 static void DdmServer_nativeSendChunk(JNIEnv* env, jclass, jint type,
                                       jbyteArray javaData, jint offset, jint length) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   ScopedByteArrayRO data(env, javaData);
   DCHECK_LE(offset + length, static_cast<int32_t>(data.size()));
   Dbg::DdmSendChunk(type, length, reinterpret_cast<const uint8_t*>(&data[offset]));
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(DdmServer, nativeSendChunk, "(I[BII)V"),
+  NATIVE_METHOD(DdmServer, nativeSendChunk, "!(I[BII)V"),
 };
 
 void register_org_apache_harmony_dalvik_ddmc_DdmServer(JNIEnv* env) {
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index eece81a..2c6d281 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -19,12 +19,12 @@
 #include "jni_internal.h"
 #include "mirror/object.h"
 #include "mirror/object-inl.h"
-#include "scoped_thread_state_change.h"
+#include "scoped_fast_native_object_access.h"
 
 namespace art {
 
 static jboolean Unsafe_compareAndSwapInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint expectedValue, jint newValue) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   byte* raw_addr = reinterpret_cast<byte*>(obj) + offset;
   volatile int32_t* address = reinterpret_cast<volatile int32_t*>(raw_addr);
@@ -34,7 +34,7 @@
 }
 
 static jboolean Unsafe_compareAndSwapLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong expectedValue, jlong newValue) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   byte* raw_addr = reinterpret_cast<byte*>(obj) + offset;
   volatile int64_t* address = reinterpret_cast<volatile int64_t*>(raw_addr);
@@ -44,7 +44,7 @@
 }
 
 static jboolean Unsafe_compareAndSwapObject(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaExpectedValue, jobject javaNewValue) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   mirror::Object* expectedValue = soa.Decode<mirror::Object*>(javaExpectedValue);
   mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
@@ -60,97 +60,97 @@
 }
 
 static jint Unsafe_getInt(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   return obj->GetField32(MemberOffset(offset), false);
 }
 
 static jint Unsafe_getIntVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   return obj->GetField32(MemberOffset(offset), true);
 }
 
 static void Unsafe_putInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   obj->SetField32(MemberOffset(offset), newValue, false);
 }
 
 static void Unsafe_putIntVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   obj->SetField32(MemberOffset(offset), newValue, true);
 }
 
 static void Unsafe_putOrderedInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   ANDROID_MEMBAR_STORE();
   obj->SetField32(MemberOffset(offset), newValue, false);
 }
 
 static jlong Unsafe_getLong(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   return obj->GetField64(MemberOffset(offset), false);
 }
 
 static jlong Unsafe_getLongVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   return obj->GetField64(MemberOffset(offset), true);
 }
 
 static void Unsafe_putLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   obj->SetField64(MemberOffset(offset), newValue, false);
 }
 
 static void Unsafe_putLongVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   obj->SetField64(MemberOffset(offset), newValue, true);
 }
 
 static void Unsafe_putOrderedLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   ANDROID_MEMBAR_STORE();
   obj->SetField64(MemberOffset(offset), newValue, false);
 }
 
 static jobject Unsafe_getObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   mirror::Object* value = obj->GetFieldObject<mirror::Object*>(MemberOffset(offset), true);
   return soa.AddLocalReference<jobject>(value);
 }
 
 static jobject Unsafe_getObject(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   mirror::Object* value = obj->GetFieldObject<mirror::Object*>(MemberOffset(offset), false);
   return soa.AddLocalReference<jobject>(value);
 }
 
 static void Unsafe_putObject(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
   obj->SetFieldObject(MemberOffset(offset), newValue, false);
 }
 
 static void Unsafe_putObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
   obj->SetFieldObject(MemberOffset(offset), newValue, true);
 }
 
 static void Unsafe_putOrderedObject(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) {
-  ScopedObjectAccess soa(env);
+  ScopedFastNativeObjectAccess soa(env);
   mirror::Object* obj = soa.Decode<mirror::Object*>(javaObj);
   mirror::Object* newValue = soa.Decode<mirror::Object*>(javaNewValue);
   ANDROID_MEMBAR_STORE();
@@ -158,24 +158,24 @@
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(Unsafe, compareAndSwapInt, "(Ljava/lang/Object;JII)Z"),
-  NATIVE_METHOD(Unsafe, compareAndSwapLong, "(Ljava/lang/Object;JJJ)Z"),
-  NATIVE_METHOD(Unsafe, compareAndSwapObject, "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z"),
-  NATIVE_METHOD(Unsafe, getIntVolatile, "(Ljava/lang/Object;J)I"),
-  NATIVE_METHOD(Unsafe, putIntVolatile, "(Ljava/lang/Object;JI)V"),
-  NATIVE_METHOD(Unsafe, getLongVolatile, "(Ljava/lang/Object;J)J"),
-  NATIVE_METHOD(Unsafe, putLongVolatile, "(Ljava/lang/Object;JJ)V"),
-  NATIVE_METHOD(Unsafe, getObjectVolatile, "(Ljava/lang/Object;J)Ljava/lang/Object;"),
-  NATIVE_METHOD(Unsafe, putObjectVolatile, "(Ljava/lang/Object;JLjava/lang/Object;)V"),
-  NATIVE_METHOD(Unsafe, getInt, "(Ljava/lang/Object;J)I"),
-  NATIVE_METHOD(Unsafe, putInt, "(Ljava/lang/Object;JI)V"),
-  NATIVE_METHOD(Unsafe, putOrderedInt, "(Ljava/lang/Object;JI)V"),
-  NATIVE_METHOD(Unsafe, getLong, "(Ljava/lang/Object;J)J"),
-  NATIVE_METHOD(Unsafe, putLong, "(Ljava/lang/Object;JJ)V"),
-  NATIVE_METHOD(Unsafe, putOrderedLong, "(Ljava/lang/Object;JJ)V"),
-  NATIVE_METHOD(Unsafe, getObject, "(Ljava/lang/Object;J)Ljava/lang/Object;"),
-  NATIVE_METHOD(Unsafe, putObject, "(Ljava/lang/Object;JLjava/lang/Object;)V"),
-  NATIVE_METHOD(Unsafe, putOrderedObject, "(Ljava/lang/Object;JLjava/lang/Object;)V"),
+  NATIVE_METHOD(Unsafe, compareAndSwapInt, "!(Ljava/lang/Object;JII)Z"),
+  NATIVE_METHOD(Unsafe, compareAndSwapLong, "!(Ljava/lang/Object;JJJ)Z"),
+  NATIVE_METHOD(Unsafe, compareAndSwapObject, "!(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z"),
+  NATIVE_METHOD(Unsafe, getIntVolatile, "!(Ljava/lang/Object;J)I"),
+  NATIVE_METHOD(Unsafe, putIntVolatile, "!(Ljava/lang/Object;JI)V"),
+  NATIVE_METHOD(Unsafe, getLongVolatile, "!(Ljava/lang/Object;J)J"),
+  NATIVE_METHOD(Unsafe, putLongVolatile, "!(Ljava/lang/Object;JJ)V"),
+  NATIVE_METHOD(Unsafe, getObjectVolatile, "!(Ljava/lang/Object;J)Ljava/lang/Object;"),
+  NATIVE_METHOD(Unsafe, putObjectVolatile, "!(Ljava/lang/Object;JLjava/lang/Object;)V"),
+  NATIVE_METHOD(Unsafe, getInt, "!(Ljava/lang/Object;J)I"),
+  NATIVE_METHOD(Unsafe, putInt, "!(Ljava/lang/Object;JI)V"),
+  NATIVE_METHOD(Unsafe, putOrderedInt, "!(Ljava/lang/Object;JI)V"),
+  NATIVE_METHOD(Unsafe, getLong, "!(Ljava/lang/Object;J)J"),
+  NATIVE_METHOD(Unsafe, putLong, "!(Ljava/lang/Object;JJ)V"),
+  NATIVE_METHOD(Unsafe, putOrderedLong, "!(Ljava/lang/Object;JJ)V"),
+  NATIVE_METHOD(Unsafe, getObject, "!(Ljava/lang/Object;J)Ljava/lang/Object;"),
+  NATIVE_METHOD(Unsafe, putObject, "!(Ljava/lang/Object;JLjava/lang/Object;)V"),
+  NATIVE_METHOD(Unsafe, putOrderedObject, "!(Ljava/lang/Object;JLjava/lang/Object;)V"),
 };
 
 void register_sun_misc_Unsafe(JNIEnv* env) {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index bdedef4..f46b794 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -977,7 +977,7 @@
     std::string mapped_name(StringPrintf(OS_SHARED_LIB_FORMAT_STR, "javacore"));
     std::string reason;
     self->TransitionFromSuspendedToRunnable();
-    if (!instance_->java_vm_->LoadNativeLibrary(mapped_name, NULL, reason)) {
+    if (!instance_->java_vm_->LoadNativeLibrary(mapped_name, NULL, &reason)) {
       LOG(FATAL) << "LoadNativeLibrary failed for \"" << mapped_name << "\": " << reason;
     }
     self->TransitionFromRunnableToSuspended(kNative);
diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h
index d3f3a88..c39cdb2 100644
--- a/runtime/scoped_thread_state_change.h
+++ b/runtime/scoped_thread_state_change.h
@@ -18,7 +18,6 @@
 #define ART_RUNTIME_SCOPED_THREAD_STATE_CHANGE_H_
 
 #include "base/casts.h"
-#include "jni_internal.h"
 #include "thread-inl.h"
 
 namespace art {
@@ -122,14 +121,14 @@
   explicit ScopedObjectAccessUnchecked(JNIEnv* env)
       LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_) ALWAYS_INLINE
       : ScopedThreadStateChange(ThreadForEnv(env), kRunnable),
-        env_(reinterpret_cast<JNIEnvExt*>(env)), vm_(env_->vm) {
+        env_(down_cast<JNIEnvExt*>(env)), vm_(env_->vm) {
     self_->VerifyStack();
   }
 
   explicit ScopedObjectAccessUnchecked(Thread* self)
       LOCKS_EXCLUDED(Locks::thread_suspend_count_lock_)
       : ScopedThreadStateChange(self, kRunnable),
-        env_(reinterpret_cast<JNIEnvExt*>(self->GetJniEnv())),
+        env_(down_cast<JNIEnvExt*>(self->GetJniEnv())),
         vm_(env_ != NULL ? env_->vm : NULL) {
     self_->VerifyStack();
   }
@@ -137,7 +136,7 @@
   // Used when we want a scoped JNI thread state but have no thread/JNIEnv. Consequently doesn't
   // change into Runnable or acquire a share on the mutator_lock_.
   explicit ScopedObjectAccessUnchecked(JavaVM* vm)
-      : ScopedThreadStateChange(), env_(NULL), vm_(reinterpret_cast<JavaVMExt*>(vm)) {}
+      : ScopedThreadStateChange(), env_(NULL), vm_(down_cast<JavaVMExt*>(vm)) {}
 
   // Here purely to force inlining.
   ~ScopedObjectAccessUnchecked() ALWAYS_INLINE {
@@ -162,6 +161,7 @@
    */
   template<typename T>
   T AddLocalReference(mirror::Object* obj) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    Locks::mutator_lock_->AssertSharedHeld(Self());
     DCHECK_EQ(thread_state_, kRunnable);  // Don't work with raw objects in non-runnable states.
     if (obj == NULL) {
       return NULL;
@@ -245,11 +245,6 @@
   }
 
  private:
-  static Thread* ThreadForEnv(JNIEnv* env) {
-    JNIEnvExt* full_env(reinterpret_cast<JNIEnvExt*>(env));
-    return full_env->self;
-  }
-
   // The full JNIEnv.
   JNIEnvExt* const env_;
   // The full JavaVM.
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index 7d28785..8449607 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -21,11 +21,19 @@
 
 #include <pthread.h>
 
+#include "base/casts.h"
 #include "base/mutex-inl.h"
 #include "cutils/atomic-inline.h"
+#include "jni_internal.h"
 
 namespace art {
 
+// Quickly access the current thread from a JNIEnv.
+static inline Thread* ThreadForEnv(JNIEnv* env) {
+  JNIEnvExt* full_env(down_cast<JNIEnvExt*>(env));
+  return full_env->self;
+}
+
 inline Thread* Thread::Current() {
   // We rely on Thread::Current returning NULL for a detached thread, so it's not obvious
   // that we can replace this with a direct %fs access on x86.