Switch to indirect references rather than direct pointers.

This also required a bit of fiddling to break cyclic dependencies,
now "jni_internal.h" needs Mutex from "thread.h".

Change-Id: I1d6fb2d801c190f72255f5e447a0a8a65cc3e673
diff --git a/src/indirect_reference_table.cc b/src/indirect_reference_table.cc
index 20f39b6..b30ce53 100644
--- a/src/indirect_reference_table.cc
+++ b/src/indirect_reference_table.cc
@@ -269,7 +269,7 @@
       segmentState.parts.topIndex = topIndex;
     } else {
       segmentState.parts.topIndex = topIndex-1;
-      LOG(INFO) << "+++ ate last entry " << topIndex-1;
+      //LOG(INFO) << "+++ ate last entry " << topIndex-1;
     }
   } else {
     /*
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 94e67a4..599b0fe 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -151,10 +151,6 @@
 
   // TODO: automate some of these checks!
 
-  if (verbose_jni) {
-    LOG(INFO) << "[calling dlopen(\"" << path << "\")...]";
-  }
-
   // This can execute slowly for a large library on a busy system, so we
   // want to switch from RUNNING to VMWAIT while it executes.  This allows
   // the GC to ignore us.
@@ -165,7 +161,7 @@
   self->SetState(old_state);
 
   if (verbose_jni) {
-    LOG(INFO) << "[dlopen(\"" << path << "\") returned " << handle << "]";
+    LOG(INFO) << "[Call to dlopen(\"" << path << "\") returned " << handle << "]";
   }
 
   if (handle == NULL) {
@@ -235,7 +231,8 @@
         result = false;
       } else {
         if (verbose_jni) {
-          LOG(INFO) << "[Returned from JNI_OnLoad in \"" << path << "\"]";
+          LOG(INFO) << "[Returned " << (result ? "successfully" : "failure")
+                    << " from JNI_OnLoad in \"" << path << "\"]";
         }
       }
     }
@@ -258,7 +255,8 @@
 // that are using a JNIEnv on the wrong thread.
 class ScopedJniThreadState {
  public:
-  explicit ScopedJniThreadState(JNIEnv* env) {
+  explicit ScopedJniThreadState(JNIEnv* env)
+      : env_(reinterpret_cast<JNIEnvExt*>(env)) {
     self_ = ThreadForEnv(env);
     self_->SetState(Thread::kRunnable);
   }
@@ -267,6 +265,10 @@
     self_->SetState(Thread::kNative);
   }
 
+  JNIEnvExt* Env() {
+    return env_;
+  }
+
   Thread* Self() {
     return self_;
   }
@@ -285,14 +287,114 @@
     return self;
   }
 
+  JNIEnvExt* env_;
   Thread* self_;
   DISALLOW_COPY_AND_ASSIGN(ScopedJniThreadState);
 };
 
+/*
+ * Add a local reference for an object to the current stack frame.  When
+ * the native function returns, the reference will be discarded.
+ *
+ * We need to allow the same reference to be added multiple times.
+ *
+ * This will be called on otherwise unreferenced objects.  We cannot do
+ * GC allocations here, and it's best if we don't grab a mutex.
+ *
+ * Returns the local reference (currently just the same pointer that was
+ * passed in), or NULL on failure.
+ */
 template<typename T>
 T AddLocalReference(ScopedJniThreadState& ts, Object* obj) {
-  UNIMPLEMENTED(WARNING);
-  return reinterpret_cast<T>(obj);
+  if (obj == NULL) {
+    return NULL;
+  }
+
+  IndirectReferenceTable& locals = ts.Env()->locals;
+
+  uint32_t cookie = IRT_FIRST_SEGMENT; // TODO
+  IndirectRef ref = locals.Add(cookie, obj);
+  if (ref == NULL) {
+    // TODO: just change Add's DCHECK to CHECK and lose this?
+    locals.Dump();
+    LOG(FATAL) << "Failed adding to JNI local reference table "
+               << "(has " << locals.Capacity() << " entries)";
+    // TODO: dvmDumpThread(dvmThreadSelf(), false);
+  }
+
+#if 0 // TODO: fix this to understand PushLocalFrame, so we can turn it on.
+  if (ts.Env()->check_jni) {
+    size_t entry_count = locals.Capacity();
+    if (entry_count > 16) {
+      std::string class_name(PrettyDescriptor(obj->GetClass()->GetDescriptor()));
+      LOG(WARNING) << "Warning: more than 16 JNI local references: "
+                   << entry_count << " (most recent was a " << class_name << ")";
+      locals.Dump();
+      // TODO: dvmDumpThread(dvmThreadSelf(), false);
+      // dvmAbort();
+    }
+  }
+#endif
+
+  if (false /*gDvmJni.workAroundAppJniBugs*/) { // TODO
+    // Hand out direct pointers to support broken old apps.
+    return reinterpret_cast<T>(obj);
+  }
+
+  return reinterpret_cast<T>(ref);
+}
+
+template<typename T>
+T Decode(ScopedJniThreadState& ts, jobject obj) {
+  if (obj == NULL) {
+    return NULL;
+  }
+
+  IndirectRef ref = reinterpret_cast<IndirectRef>(obj);
+  IndirectRefKind kind = GetIndirectRefKind(ref);
+  Object* result;
+  switch (kind) {
+  case kLocal:
+    {
+      IndirectReferenceTable& locals = ts.Env()->locals;
+      result = locals.Get(ref);
+      break;
+    }
+  case kGlobal:
+    {
+      JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+      IndirectReferenceTable& globals = vm->globals;
+      MutexLock mu(&vm->globals_lock);
+      result = globals.Get(ref);
+      break;
+    }
+  case kWeakGlobal:
+    {
+      JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+      IndirectReferenceTable& weak_globals = vm->weak_globals;
+      MutexLock mu(&vm->weak_globals_lock);
+      result = weak_globals.Get(ref);
+      if (result == kClearedJniWeakGlobal) {
+        // This is a special case where it's okay to return NULL.
+        return NULL;
+      }
+      break;
+    }
+  case kInvalid:
+  default:
+    if (false /*gDvmJni.workAroundAppJniBugs*/) { // TODO
+      // Assume an invalid local reference is actually a direct pointer.
+      return reinterpret_cast<T>(obj);
+    }
+    LOG(FATAL) << "Invalid indirect reference " << obj;
+    return reinterpret_cast<T>(kInvalidIndirectRefObject);
+  }
+
+  if (result == NULL) {
+    LOG(FATAL) << "JNI ERROR (app bug): use of deleted " << kind << ": "
+               << obj;
+  }
+  return reinterpret_cast<T>(result);
 }
 
 void CreateInvokeStub(Assembler* assembler, Method* method);
@@ -318,7 +420,7 @@
   return true;
 }
 
-byte* CreateArgArray(Method* method, va_list ap) {
+byte* CreateArgArray(ScopedJniThreadState& ts, Method* method, va_list ap) {
   size_t num_bytes = method->NumArgArrayBytes();
   scoped_array<byte> arg_array(new byte[num_bytes]);
   const StringPiece& shorty = method->GetShorty();
@@ -337,8 +439,7 @@
         offset += 4;
         break;
       case 'L': {
-        // TODO: DecodeReference
-        Object* obj = reinterpret_cast<Object*>(va_arg(ap, jobject));
+        Object* obj = Decode<Object*>(ts, va_arg(ap, jobject));
         *reinterpret_cast<Object**>(&arg_array[offset]) = obj;
         offset += sizeof(Object*);
         break;
@@ -356,7 +457,7 @@
   return arg_array.release();
 }
 
-byte* CreateArgArray(Method* method, jvalue* args) {
+byte* CreateArgArray(ScopedJniThreadState& ts, Method* method, jvalue* args) {
   size_t num_bytes = method->NumArgArrayBytes();
   scoped_array<byte> arg_array(new byte[num_bytes]);
   const StringPiece& shorty = method->GetShorty();
@@ -375,8 +476,7 @@
         offset += 4;
         break;
       case 'L': {
-        // TODO: DecodeReference
-        Object* obj = reinterpret_cast<Object*>(args[i - 1].l);
+        Object* obj = Decode<Object*>(ts, args[i - 1].l);
         *reinterpret_cast<Object**>(&arg_array[offset]) = obj;
         offset += sizeof(Object*);
         break;
@@ -394,8 +494,8 @@
   return arg_array.release();
 }
 
-JValue InvokeWithArgArray(Thread* self, Object* obj, jmethodID method_id,
-                          byte* args) {
+JValue InvokeWithArgArray(ScopedJniThreadState& ts,
+    Object* obj, jmethodID method_id, byte* args) {
   // TODO: DecodeReference
   Method* method = reinterpret_cast<Method*>(method_id);
   // Call the invoke stub associated with the method
@@ -403,22 +503,22 @@
   const Method::InvokeStub* stub = method->GetInvokeStub();
   CHECK(stub != NULL);
   JValue result;
-  (*stub)(method, obj, self, args, &result);
+  (*stub)(method, obj, ts.Self(), args, &result);
   return result;
 }
 
-JValue InvokeWithJValues(Thread* self, Object* obj, jmethodID method_id,
-                         jvalue* args) {
+JValue InvokeWithJValues(ScopedJniThreadState& ts,
+    Object* obj, jmethodID method_id, jvalue* args) {
   Method* method = reinterpret_cast<Method*>(method_id);
-  scoped_array<byte> arg_array(CreateArgArray(method, args));
-  return InvokeWithArgArray(self, obj, method_id, arg_array.get());
+  scoped_array<byte> arg_array(CreateArgArray(ts, method, args));
+  return InvokeWithArgArray(ts, obj, method_id, arg_array.get());
 }
 
-JValue InvokeWithVarArgs(Thread* self, Object* obj, jmethodID method_id,
-                         va_list args) {
+JValue InvokeWithVarArgs(ScopedJniThreadState& ts,
+    Object* obj, jmethodID method_id, va_list args) {
   Method* method = reinterpret_cast<Method*>(method_id);
-  scoped_array<byte> arg_array(CreateArgArray(method, args));
-  return InvokeWithArgArray(self, obj, method_id, arg_array.get());
+  scoped_array<byte> arg_array(CreateArgArray(ts, method, args));
+  return InvokeWithArgArray(ts, obj, method_id, arg_array.get());
 }
 
 jint GetVersion(JNIEnv* env) {
@@ -559,7 +659,7 @@
 
 jobject PopLocalFrame(JNIEnv* env, jobject res) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(WARNING) << "ignoring PopLocalFrame";
+  UNIMPLEMENTED(WARNING) << "ignoring PopLocalFrame " << res;
   return res;
 }
 
@@ -576,7 +676,23 @@
 
 void DeleteLocalRef(JNIEnv* env, jobject obj) {
   ScopedJniThreadState ts(env);
-  UNIMPLEMENTED(WARNING);
+
+  if (obj == NULL) {
+    return;
+  }
+
+  IndirectReferenceTable& locals = ts.Env()->locals;
+
+  uint32_t cookie = IRT_FIRST_SEGMENT; // TODO
+  if (!locals.Remove(cookie, obj)) {
+    // Attempting to delete a local reference that is not in the
+    // topmost local reference frame is a no-op.  DeleteLocalRef returns
+    // void and doesn't throw any exceptions, but we should probably
+    // complain about it so the user will notice that things aren't
+    // going quite the way they expect.
+    LOG(WARNING) << "JNI WARNING: DeleteLocalRef(" << obj << ") "
+                 << "failed to find entry";
+  }
 }
 
 jboolean IsSameObject(JNIEnv* env, jobject obj1, jobject obj2) {
@@ -636,10 +752,8 @@
     // NB. JNI is different from regular Java instanceof in this respect
     return JNI_TRUE;
   } else {
-    // TODO: retrieve handle value for object
-    Object* obj = reinterpret_cast<Object*>(jobj);
-    // TODO: retrieve handle value for class
-    Class* klass = reinterpret_cast<Class*>(clazz);
+    Object* obj = Decode<Object*>(ts, jobj);
+    Class* klass = Decode<Class*>(ts, clazz);
     return Object::InstanceOf(obj, klass) ? JNI_TRUE : JNI_FALSE;
   }
 }
@@ -647,8 +761,7 @@
 jmethodID GetMethodID(JNIEnv* env,
     jclass clazz, const char* name, const char* sig) {
   ScopedJniThreadState ts(env);
-  // TODO: retrieve handle value for class
-  Class* klass = reinterpret_cast<Class*>(clazz);
+  Class* klass = Decode<Class*>(ts, clazz);
   if (!klass->IsInitialized()) {
     // TODO: initialize the class
   }
@@ -1198,8 +1311,7 @@
 jmethodID GetStaticMethodID(JNIEnv* env,
     jclass clazz, const char* name, const char* sig) {
   ScopedJniThreadState ts(env);
-  // TODO: DecodeReference
-  Class* klass = reinterpret_cast<Class*>(clazz);
+  Class* klass = Decode<Class*>(ts, clazz);
   if (!klass->IsInitialized()) {
     // TODO: initialize the class
   }
@@ -1238,21 +1350,21 @@
   ScopedJniThreadState ts(env);
   va_list ap;
   va_start(ap, methodID);
-  JValue result = InvokeWithVarArgs(ts.Self(), NULL, methodID, ap);
+  JValue result = InvokeWithVarArgs(ts, NULL, methodID, ap);
   return AddLocalReference<jobject>(ts, result.l);
 }
 
 jobject CallStaticObjectMethodV(JNIEnv* env,
     jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  JValue result = InvokeWithVarArgs(ts.Self(), NULL, methodID, args);
+  JValue result = InvokeWithVarArgs(ts, NULL, methodID, args);
   return AddLocalReference<jobject>(ts, result.l);
 }
 
 jobject CallStaticObjectMethodA(JNIEnv* env,
     jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  JValue result = InvokeWithJValues(ts.Self(), NULL, methodID, args);
+  JValue result = InvokeWithJValues(ts, NULL, methodID, args);
   return AddLocalReference<jobject>(ts, result.l);
 }
 
@@ -1261,171 +1373,171 @@
   ScopedJniThreadState ts(env);
   va_list ap;
   va_start(ap, methodID);
-  return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).z;
+  return InvokeWithVarArgs(ts, NULL, methodID, ap).z;
 }
 
 jboolean CallStaticBooleanMethodV(JNIEnv* env,
     jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).z;
+  return InvokeWithVarArgs(ts, NULL, methodID, args).z;
 }
 
 jboolean CallStaticBooleanMethodA(JNIEnv* env,
     jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  return InvokeWithJValues(ts.Self(), NULL, methodID, args).z;
+  return InvokeWithJValues(ts, NULL, methodID, args).z;
 }
 
 jbyte CallStaticByteMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
   va_list ap;
   va_start(ap, methodID);
-  return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).b;
+  return InvokeWithVarArgs(ts, NULL, methodID, ap).b;
 }
 
 jbyte CallStaticByteMethodV(JNIEnv* env,
     jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).b;
+  return InvokeWithVarArgs(ts, NULL, methodID, args).b;
 }
 
 jbyte CallStaticByteMethodA(JNIEnv* env,
     jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  return InvokeWithJValues(ts.Self(), NULL, methodID, args).b;
+  return InvokeWithJValues(ts, NULL, methodID, args).b;
 }
 
 jchar CallStaticCharMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
   va_list ap;
   va_start(ap, methodID);
-  return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).c;
+  return InvokeWithVarArgs(ts, NULL, methodID, ap).c;
 }
 
 jchar CallStaticCharMethodV(JNIEnv* env,
     jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).c;
+  return InvokeWithVarArgs(ts, NULL, methodID, args).c;
 }
 
 jchar CallStaticCharMethodA(JNIEnv* env,
     jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  return InvokeWithJValues(ts.Self(), NULL, methodID, args).c;
+  return InvokeWithJValues(ts, NULL, methodID, args).c;
 }
 
 jshort CallStaticShortMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
   va_list ap;
   va_start(ap, methodID);
-  return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).s;
+  return InvokeWithVarArgs(ts, NULL, methodID, ap).s;
 }
 
 jshort CallStaticShortMethodV(JNIEnv* env,
     jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).s;
+  return InvokeWithVarArgs(ts, NULL, methodID, args).s;
 }
 
 jshort CallStaticShortMethodA(JNIEnv* env,
     jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  return InvokeWithJValues(ts.Self(), NULL, methodID, args).s;
+  return InvokeWithJValues(ts, NULL, methodID, args).s;
 }
 
 jint CallStaticIntMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
   va_list ap;
   va_start(ap, methodID);
-  return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).i;
+  return InvokeWithVarArgs(ts, NULL, methodID, ap).i;
 }
 
 jint CallStaticIntMethodV(JNIEnv* env,
     jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).i;
+  return InvokeWithVarArgs(ts, NULL, methodID, args).i;
 }
 
 jint CallStaticIntMethodA(JNIEnv* env,
     jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  return InvokeWithJValues(ts.Self(), NULL, methodID, args).i;
+  return InvokeWithJValues(ts, NULL, methodID, args).i;
 }
 
 jlong CallStaticLongMethod(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
   va_list ap;
   va_start(ap, methodID);
-  return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).j;
+  return InvokeWithVarArgs(ts, NULL, methodID, ap).j;
 }
 
 jlong CallStaticLongMethodV(JNIEnv* env,
     jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).j;
+  return InvokeWithVarArgs(ts, NULL, methodID, args).j;
 }
 
 jlong CallStaticLongMethodA(JNIEnv* env,
     jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  return InvokeWithJValues(ts.Self(), NULL, methodID, args).j;
+  return InvokeWithJValues(ts, NULL, methodID, args).j;
 }
 
 jfloat CallStaticFloatMethod(JNIEnv* env, jclass cls, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
   va_list ap;
   va_start(ap, methodID);
-  return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).f;
+  return InvokeWithVarArgs(ts, NULL, methodID, ap).f;
 }
 
 jfloat CallStaticFloatMethodV(JNIEnv* env,
     jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).f;
+  return InvokeWithVarArgs(ts, NULL, methodID, args).f;
 }
 
 jfloat CallStaticFloatMethodA(JNIEnv* env,
     jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  return InvokeWithJValues(ts.Self(), NULL, methodID, args).f;
+  return InvokeWithJValues(ts, NULL, methodID, args).f;
 }
 
 jdouble CallStaticDoubleMethod(JNIEnv* env, jclass cls, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
   va_list ap;
   va_start(ap, methodID);
-  return InvokeWithVarArgs(ts.Self(), NULL, methodID, ap).d;
+  return InvokeWithVarArgs(ts, NULL, methodID, ap).d;
 }
 
 jdouble CallStaticDoubleMethodV(JNIEnv* env,
     jclass clazz, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  return InvokeWithVarArgs(ts.Self(), NULL, methodID, args).d;
+  return InvokeWithVarArgs(ts, NULL, methodID, args).d;
 }
 
 jdouble CallStaticDoubleMethodA(JNIEnv* env,
     jclass clazz, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  return InvokeWithJValues(ts.Self(), NULL, methodID, args).d;
+  return InvokeWithJValues(ts, NULL, methodID, args).d;
 }
 
 void CallStaticVoidMethod(JNIEnv* env, jclass cls, jmethodID methodID, ...) {
   ScopedJniThreadState ts(env);
   va_list ap;
   va_start(ap, methodID);
-  InvokeWithVarArgs(ts.Self(), NULL, methodID, ap);
+  InvokeWithVarArgs(ts, NULL, methodID, ap);
 }
 
 void CallStaticVoidMethodV(JNIEnv* env,
     jclass cls, jmethodID methodID, va_list args) {
   ScopedJniThreadState ts(env);
-  InvokeWithVarArgs(ts.Self(), NULL, methodID, args);
+  InvokeWithVarArgs(ts, NULL, methodID, args);
 }
 
 void CallStaticVoidMethodA(JNIEnv* env,
     jclass cls, jmethodID methodID, jvalue* args) {
   ScopedJniThreadState ts(env);
-  InvokeWithJValues(ts.Self(), NULL, methodID, args);
+  InvokeWithJValues(ts, NULL, methodID, args);
 }
 
 jfieldID GetStaticFieldID(JNIEnv* env,
@@ -1608,9 +1720,8 @@
 void SetObjectArrayElement(JNIEnv* env,
     jobjectArray java_array, jsize index, jobject java_value) {
   ScopedJniThreadState ts(env);
-  // TODO: DecodeReference
-  ObjectArray<Object>* array = reinterpret_cast<ObjectArray<Object>*>(java_array);
-  Object* value = reinterpret_cast<Object*>(java_value);
+  ObjectArray<Object>* array = Decode<ObjectArray<Object>*>(ts, java_array);
+  Object* value = Decode<Object*>(ts, java_value);
   array->Set(index, value);
 }
 
@@ -1661,8 +1772,7 @@
   CHECK_GE(length, 0); // TODO: ReportJniError
 
   // Compute the array class corresponding to the given element class.
-  // TODO: DecodeReference
-  Class* element_class = reinterpret_cast<Class*>(element_jclass);
+  Class* element_class = Decode<Class*>(ts, element_jclass);
   std::string descriptor;
   descriptor += "[";
   descriptor += element_class->GetDescriptor().ToString();
@@ -1884,8 +1994,7 @@
 jint RegisterNatives(JNIEnv* env,
     jclass clazz, const JNINativeMethod* methods, jint nMethods) {
   ScopedJniThreadState ts(env);
-  // TODO: retrieve handle value for class
-  Class* klass = reinterpret_cast<Class*>(clazz);
+  Class* klass = Decode<Class*>(ts, clazz);
   for(int i = 0; i < nMethods; i++) {
     const char* name = methods[i].name;
     const char* sig = methods[i].signature;
@@ -2421,7 +2530,9 @@
       check_jni(check_jni),
       verbose_jni(verbose_jni),
       pin_table("pin table", kPinTableInitialSize, kPinTableMaxSize),
+      globals_lock("JNI global reference table"),
       globals(kGlobalsInitial, kGlobalsMax, kGlobal),
+      weak_globals_lock("JNI weak global reference table"),
       weak_globals(kWeakGlobalsInitial, kWeakGlobalsMax, kWeakGlobal) {
 }
 
diff --git a/src/jni_internal.h b/src/jni_internal.h
index 5d89dd8..74366d3 100644
--- a/src/jni_internal.h
+++ b/src/jni_internal.h
@@ -9,6 +9,7 @@
 #include "indirect_reference_table.h"
 #include "macros.h"
 #include "reference_table.h"
+#include "thread.h"
 
 #include <map>
 #include <string>
@@ -17,7 +18,6 @@
 
 class Runtime;
 class SharedLibrary;
-class Thread;
 
 struct JavaVMExt {
   JavaVMExt(Runtime* runtime, bool check_jni, bool verbose_jni);
@@ -53,9 +53,11 @@
   ReferenceTable pin_table;
 
   // JNI global references.
+  Mutex globals_lock;
   IndirectReferenceTable globals;
 
   // JNI weak global references.
+  Mutex weak_globals_lock;
   IndirectReferenceTable weak_globals;
 
   std::map<std::string, SharedLibrary*> libraries;
diff --git a/src/runtime.cc b/src/runtime.cc
index 43427df..335396d 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -9,6 +9,7 @@
 
 #include "class_linker.h"
 #include "heap.h"
+#include "jni_internal.h"
 #include "scoped_ptr.h"
 #include "thread.h"
 
@@ -17,10 +18,11 @@
 Runtime* Runtime::instance_ = NULL;
 
 Runtime::~Runtime() {
-  // TODO: use a smart pointer instead.
+  // TODO: use smart pointers instead. (we'll need the pimpl idiom.)
   delete class_linker_;
   Heap::Destroy();
   delete thread_list_;
+  delete java_vm_;
   // TODO: acquire a static mutex on Runtime to avoid racing.
   CHECK(instance_ == NULL || instance_ == this);
   instance_ = NULL;
@@ -336,7 +338,7 @@
   }
 
   bool verbose_jni = options->verbose_.find("jni") != options->verbose_.end();
-  java_vm_.reset(new JavaVMExt(this, options->check_jni_, verbose_jni));
+  java_vm_ = new JavaVMExt(this, options->check_jni_, verbose_jni);
 
   if (!Thread::Init()) {
     return false;
diff --git a/src/runtime.h b/src/runtime.h
index e9d373d..9879023 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -3,12 +3,15 @@
 #ifndef ART_SRC_RUNTIME_H_
 #define ART_SRC_RUNTIME_H_
 
+#include <stdio.h>
+
 #include <string>
 #include <utility>
 #include <vector>
 
+#include <jni.h>
+
 #include "globals.h"
-#include "jni_internal.h"
 #include "macros.h"
 #include "scoped_ptr.h"
 #include "stringpiece.h"
@@ -19,6 +22,7 @@
 class ClassLinker;
 class DexFile;
 class Heap;
+class JavaVMExt;
 class String;
 class ThreadList;
 
@@ -84,7 +88,7 @@
   }
 
   JavaVMExt* GetJavaVM() const {
-    return java_vm_.get();
+    return java_vm_;
   }
 
  private:
@@ -102,7 +106,7 @@
 
   ClassLinker* class_linker_;
 
-  scoped_ptr<JavaVMExt> java_vm_;
+  JavaVMExt* java_vm_;
 
   // Hooks supported by JNI_CreateJavaVM
   jint (*vfprintf_)(FILE* stream, const char* format, va_list ap);
diff --git a/src/thread.cc b/src/thread.cc
index 1001b29..eb7a4f7 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -9,6 +9,7 @@
 #include <list>
 
 #include "class_linker.h"
+#include "jni_internal.h"
 #include "object.h"
 #include "runtime.h"
 #include "utils.h"
diff --git a/src/thread.h b/src/thread.h
index 4264a1a..71fbd48c 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -7,7 +7,6 @@
 #include <list>
 
 #include "globals.h"
-#include "jni_internal.h"
 #include "logging.h"
 #include "macros.h"
 #include "mem_map.h"