Add back the work-arounds for broken apps that assume JNI uses direct references.

Note that we'll need to add support for this to jni_compiler too.

Change-Id: I7e31ab4314ba913cde4629544addd0aad9a89b3b
diff --git a/src/check_jni.cc b/src/check_jni.cc
index 130acef..93d3b31 100644
--- a/src/check_jni.cc
+++ b/src/check_jni.cc
@@ -21,6 +21,7 @@
 
 #include "class_linker.h"
 #include "logging.h"
+#include "scoped_jni_thread_state.h"
 #include "thread.h"
 
 namespace art {
@@ -52,50 +53,6 @@
  * ===========================================================================
  */
 
-// TODO: remove this ODR violation!
-class ScopedJniThreadState {
- public:
-  explicit ScopedJniThreadState(JNIEnv* env)
-  : env_(reinterpret_cast<JNIEnvExt*>(env)) {
-    self_ = ThreadForEnv(env);
-    self_->SetState(Thread::kRunnable);
-  }
-
-  ~ScopedJniThreadState() {
-    self_->SetState(Thread::kNative);
-  }
-
-  JNIEnvExt* Env() {
-    return env_;
-  }
-
-  Thread* Self() {
-    return self_;
-  }
-
-  JavaVMExt* Vm() {
-    return env_->vm;
-  }
-
- private:
-  static Thread* ThreadForEnv(JNIEnv* env) {
-    // TODO: need replacement for gDvmJni.
-    bool workAroundAppJniBugs = true;
-    Thread* env_self = reinterpret_cast<JNIEnvExt*>(env)->self;
-    Thread* self = workAroundAppJniBugs ? Thread::Current() : env_self;
-    if (self != env_self) {
-      LOG(ERROR) << "JNI ERROR: JNIEnv for " << *env_self
-                 << " used on " << *self;
-      // TODO: dump stack
-    }
-    return self;
-  }
-
-  JNIEnvExt* env_;
-  Thread* self_;
-  DISALLOW_COPY_AND_ASSIGN(ScopedJniThreadState);
-};
-
 template<typename T>
 T Decode(ScopedJniThreadState& ts, jobject obj) {
   return reinterpret_cast<T>(ts.Self()->DecodeJObject(obj));
@@ -802,7 +759,7 @@
 
     Object* o = Decode<Object*>(ts, java_object);
     if (o != NULL && !Heap::IsHeapAddress(o)) {
-      // TODO: when we remove workAroundAppJniBugs, this should be impossible.
+      // TODO: when we remove work_around_app_jni_bugs, this should be impossible.
       LOG(ERROR) << "JNI ERROR: native code passing in reference to invalid " << GetIndirectRefKind(java_object) << ": " << java_object;
       JniAbort();
     }
@@ -837,7 +794,7 @@
     if (mEnv != threadEnv) {
       LOG(ERROR) << "JNI ERROR: thread " << *self << " using JNIEnv* from thread " << *mEnv->self;
       // If we're keeping broken code limping along, we need to suppress the abort...
-      if (true/* TODO: !gDvmJni.workAroundAppJniBugs*/) {
+      if (!mEnv->work_around_app_jni_bugs) {
         JniAbort();
         return;
       }
diff --git a/src/indirect_reference_table.cc b/src/indirect_reference_table.cc
index 6e5446f..083a499 100644
--- a/src/indirect_reference_table.cc
+++ b/src/indirect_reference_table.cc
@@ -228,11 +228,10 @@
   DCHECK_GE(segmentState.parts.numHoles, prevState.parts.numHoles);
 
   int idx = ExtractIndex(iref);
-  bool workAroundAppJniBugs = false;
 
-  if (GetIndirectRefKind(iref) == kSirtOrInvalid /*&& gDvmJni.workAroundAppJniBugs*/) { // TODO
+  JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+  if (GetIndirectRefKind(iref) == kSirtOrInvalid || vm->work_around_app_jni_bugs) {
     idx = LinearScan(iref, bottomIndex, topIndex, table_);
-    workAroundAppJniBugs = true;
     if (idx == -1) {
       LOG(WARNING) << "trying to work around app JNI bugs, but didn't find " << iref << " in table!";
       return false;
@@ -253,7 +252,7 @@
   if (idx == topIndex-1) {
     // Top-most entry.  Scan up and consume holes.
 
-    if (workAroundAppJniBugs == false && !CheckEntry("remove", iref, idx)) {
+    if (!vm->work_around_app_jni_bugs && !CheckEntry("remove", iref, idx)) {
       return false;
     }
 
@@ -284,7 +283,7 @@
       LOG(INFO) << "--- WEIRD: removing null entry " << idx;
       return false;
     }
-    if (workAroundAppJniBugs == false && !CheckEntry("remove", iref, idx)) {
+    if (!vm->work_around_app_jni_bugs && !CheckEntry("remove", iref, idx)) {
       return false;
     }
 
diff --git a/src/indirect_reference_table.h b/src/indirect_reference_table.h
index 4a8833c..86c1293 100644
--- a/src/indirect_reference_table.h
+++ b/src/indirect_reference_table.h
@@ -275,7 +275,7 @@
     return table_[ExtractIndex(iref)];
   }
 
-  // TODO: only used for workAroundAppJniBugs support.
+  // TODO: remove when we remove work_around_app_jni_bugs support.
   bool Contains(IndirectRef iref) const;
 
   /*
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index cc1d8db..9213056 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -18,6 +18,7 @@
 #include "logging.h"
 #include "object.h"
 #include "runtime.h"
+#include "scoped_jni_thread_state.h"
 #include "stringpiece.h"
 #include "thread.h"
 
@@ -50,54 +51,6 @@
 
 namespace {
 
-// Entry/exit processing for all JNI calls.
-//
-// This performs the necessary thread state switching, lets us amortize the
-// cost of working out the current thread, and lets us check (and repair) apps
-// that are using a JNIEnv on the wrong thread.
-class ScopedJniThreadState {
- public:
-  explicit ScopedJniThreadState(JNIEnv* env)
-      : env_(reinterpret_cast<JNIEnvExt*>(env)) {
-    self_ = ThreadForEnv(env);
-    self_->SetState(Thread::kRunnable);
-  }
-
-  ~ScopedJniThreadState() {
-    self_->SetState(Thread::kNative);
-  }
-
-  JNIEnvExt* Env() {
-    return env_;
-  }
-
-  Thread* Self() {
-    return self_;
-  }
-
-  JavaVMExt* Vm() {
-    return env_->vm;
-  }
-
- private:
-  static Thread* ThreadForEnv(JNIEnv* env) {
-    // TODO: need replacement for gDvmJni.
-    bool workAroundAppJniBugs = true;
-    Thread* env_self = reinterpret_cast<JNIEnvExt*>(env)->self;
-    Thread* self = workAroundAppJniBugs ? Thread::Current() : env_self;
-    if (self != env_self) {
-      LOG(ERROR) << "JNI ERROR: JNIEnv for " << *env_self
-          << " used on " << *self;
-      // TODO: dump stack
-    }
-    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.
@@ -142,7 +95,7 @@
   }
 #endif
 
-  if (false /*gDvmJni.workAroundAppJniBugs*/) { // TODO
+  if (ts.Env()->work_around_app_jni_bugs) {
     // Hand out direct pointers to support broken old apps.
     return reinterpret_cast<T>(obj);
   }
@@ -2313,10 +2266,13 @@
         return JNILocalRefType;
       }
 
+      if (!ts.Env()->work_around_app_jni_bugs) {
+        return JNIInvalidRefType;
+      }
+
       // If we're handing out direct pointers, check whether it's a direct pointer
       // to a local reference.
-      // TODO: replace 'false' with the replacement for gDvmJni.workAroundAppJniBugs
-      if (false && Decode<Object*>(ts, java_object) == reinterpret_cast<Object*>(java_object)) {
+      if (Decode<Object*>(ts, java_object) == reinterpret_cast<Object*>(java_object)) {
         if (ts.Env()->locals.Contains(java_object)) {
           return JNILocalRefType;
         }
@@ -2573,6 +2529,7 @@
     : self(self),
       vm(vm),
       check_jni(vm->check_jni),
+      work_around_app_jni_bugs(vm->work_around_app_jni_bugs),
       critical(false),
       monitors("monitors", kMonitorsInitial, kMonitorsMax),
       locals(kLocalsInitial, kLocalsMax, kLocal) {
@@ -2698,7 +2655,8 @@
       check_jni_abort_hook(NULL),
       check_jni(check_jni),
       verbose_jni(verbose_jni),
-      force_copy(false), // TODO
+      force_copy(false), // TODO: add a way to enable this
+      work_around_app_jni_bugs(false), // TODO: add a way to enable this
       pins_lock(Mutex::Create("JNI pin table lock")),
       pin_table("pin table", kPinTableInitialSize, kPinTableMaxSize),
       globals_lock(Mutex::Create("JNI global reference table lock")),
diff --git a/src/jni_internal.h b/src/jni_internal.h
index a4362d3..05df3e4 100644
--- a/src/jni_internal.h
+++ b/src/jni_internal.h
@@ -49,6 +49,9 @@
   bool verbose_jni;
   bool force_copy;
 
+  // Used to provide compatibility for apps that assumed direct references.
+  bool work_around_app_jni_bugs;
+
   // Used to hold references to pinned primitive arrays.
   Mutex* pins_lock;
   ReferenceTable pin_table;
@@ -75,7 +78,9 @@
   Thread* self;
   JavaVMExt* vm;
 
+  // Frequently-accessed fields cached from JavaVM.
   bool check_jni;
+  bool work_around_app_jni_bugs;
 
   // How many nested "critical" JNI calls are we in?
   int critical;
diff --git a/src/scoped_jni_thread_state.h b/src/scoped_jni_thread_state.h
new file mode 100644
index 0000000..dd39cc3
--- /dev/null
+++ b/src/scoped_jni_thread_state.h
@@ -0,0 +1,55 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+// Author: enh@google.com (Elliott Hughes)
+
+#include "jni_internal.h"
+
+namespace art {
+
+// Entry/exit processing for all JNI calls.
+//
+// This performs the necessary thread state switching, lets us amortize the
+// cost of working out the current thread, and lets us check (and repair) apps
+// that are using a JNIEnv on the wrong thread.
+class ScopedJniThreadState {
+ public:
+  explicit ScopedJniThreadState(JNIEnv* env)
+      : env_(reinterpret_cast<JNIEnvExt*>(env)) {
+    self_ = ThreadForEnv(env);
+    self_->SetState(Thread::kRunnable);
+  }
+
+  ~ScopedJniThreadState() {
+    self_->SetState(Thread::kNative);
+  }
+
+  JNIEnvExt* Env() {
+    return env_;
+  }
+
+  Thread* Self() {
+    return self_;
+  }
+
+  JavaVMExt* Vm() {
+    return env_->vm;
+  }
+
+ private:
+  static Thread* ThreadForEnv(JNIEnv* env) {
+    JNIEnvExt* full_env(reinterpret_cast<JNIEnvExt*>(env));
+    Thread* env_self = full_env->self;
+    Thread* self = full_env->work_around_app_jni_bugs ? Thread::Current() : env_self;
+    if (self != env_self) {
+      LOG(ERROR) << "JNI ERROR: JNIEnv for " << *env_self
+                 << " used on " << *self;
+      // TODO: dump stack
+    }
+    return self;
+  }
+
+  JNIEnvExt* env_;
+  Thread* self_;
+  DISALLOW_COPY_AND_ASSIGN(ScopedJniThreadState);
+};
+
+}  // namespace art
diff --git a/src/thread.cc b/src/thread.cc
index c90c745..9d2862d 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -299,7 +299,7 @@
     // Check if this is a local reference in the SIRT
     if (SirtContains(obj)) {
       result = *reinterpret_cast<Object**>(obj); // Read from SIRT
-    } else if (false /*gDvmJni.workAroundAppJniBugs*/) { // TODO
+    } else if (jni_env_->work_around_app_jni_bugs) {
       // Assume an invalid local reference is actually a direct pointer.
       result = reinterpret_cast<Object*>(obj);
     } else {