Support for local references allocated in SHBs.

Local references passed in the stack handle block by the managed to
native bridge code generated jni_compiler need support for decoding. Add
support and extend jni_compiler_test to check the validity of jobject
arguments in the stack handle block.

Change-Id: Ibba60451c21f6e41023b8d837310f15ea69c44f8
diff --git a/src/jni_compiler_test.cc b/src/jni_compiler_test.cc
index 39ba6ed..8a5155f 100644
--- a/src/jni_compiler_test.cc
+++ b/src/jni_compiler_test.cc
@@ -85,8 +85,7 @@
   EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
   EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
   EXPECT_TRUE(thisObj != NULL);
-  // TODO: check JNIEnv and thisObj are sane
-  // EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
+  EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
   gJava_MyClass_foo_calls++;
 }
 
@@ -106,8 +105,7 @@
   EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
   EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
   EXPECT_TRUE(thisObj != NULL);
-  // TODO: check JNIEnv and thisObj are sane
-  // EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
+  EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
   gJava_MyClass_fooI_calls++;
   return x;
 }
@@ -131,8 +129,7 @@
   EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
   EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
   EXPECT_TRUE(thisObj != NULL);
-  // TODO: check JNIEnv and thisObj are sane
-  // EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
+  EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
   gJava_MyClass_fooII_calls++;
   return x - y;  // non-commutative operator
 }
@@ -157,8 +154,7 @@
   EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
   EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
   EXPECT_TRUE(thisObj != NULL);
-  // TODO: check JNIEnv and thisObj are sane
-  // EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
+  EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
   gJava_MyClass_fooDD_calls++;
   return x - y;  // non-commutative operator
 }
@@ -186,8 +182,7 @@
   EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
   EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
   EXPECT_TRUE(thisObj != NULL);
-  // TODO: check JNIEnv and thisObj are sane
-  // EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
+  EXPECT_TRUE(env->IsInstanceOf(thisObj, JniCompilerTest::jklass_));
   gJava_MyClass_fooIOO_calls++;
   switch (x) {
     case 1:
@@ -237,8 +232,7 @@
   EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
   EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
   EXPECT_TRUE(klass != NULL);
-  // TODO: check JNIEnv and klass are sane
-  // EXPECT_TRUE(env->IsInstanceOf(JniCompilerTest::jobj_, klass));
+  EXPECT_TRUE(env->IsInstanceOf(JniCompilerTest::jobj_, klass));
   gJava_MyClass_fooSIOO_calls++;
   switch (x) {
     case 1:
@@ -289,8 +283,7 @@
   EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
   EXPECT_EQ(Thread::Current()->GetJniEnv(), env);
   EXPECT_TRUE(klass != NULL);
-  // TODO: check JNIEnv and klass are sane
-  // EXPECT_TRUE(env->IsInstanceOf(JniCompilerTest::jobj_, klass));
+  EXPECT_TRUE(env->IsInstanceOf(JniCompilerTest::jobj_, klass));
   gJava_MyClass_fooSSIOO_calls++;
   switch (x) {
     case 1:
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index dbb2674..733a307 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -283,6 +283,11 @@
     }
   case kInvalid:
   default:
+    // TODO: make stack handle blocks more efficient
+    // Check if this is a local reference in a stack handle block
+    if (ts.Self()->ShbContains(obj)) {
+      return *reinterpret_cast<T*>(obj); // Read from stack handle block
+    }
     if (false /*gDvmJni.workAroundAppJniBugs*/) { // TODO
       // Assume an invalid local reference is actually a direct pointer.
       return reinterpret_cast<T>(obj);
diff --git a/src/thread.cc b/src/thread.cc
index dd333d7..1cb037e 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -135,6 +135,27 @@
   return true;
 }
 
+size_t Thread::NumShbHandles() {
+  size_t count = 0;
+  for (StackHandleBlock* cur = top_shb_; cur; cur = cur->Link()) {
+    count += cur->NumberOfReferences();
+  }
+  return count;
+}
+
+bool Thread::ShbContains(jobject obj) {
+  Object **shb_entry = reinterpret_cast<Object**>(obj);
+  for (StackHandleBlock* cur = top_shb_; cur; cur = cur->Link()) {
+    size_t num_refs = cur->NumberOfReferences();
+    DCHECK_GT(num_refs, 0u); // A SHB should always have a jobject/jclass
+    if ((&cur->Handles()[0] >= shb_entry) &&
+        (shb_entry <= (&cur->Handles()[num_refs-1]))) {
+      return true;
+    }
+  }
+  return false;
+}
+
 void ThrowNewException(Thread* thread, const char* exception_class_name, const char* msg) {
   CHECK(thread != NULL);
 
diff --git a/src/thread.h b/src/thread.h
index 66b5e28..ac332e0 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -80,6 +80,10 @@
     return link_;
   }
 
+  Object** Handles() {
+    return handles_;
+  }
+
   // Offset of length within SHB, used by generated code
   static size_t NumberOfReferencesOffset() {
     return OFFSETOF_MEMBER(StackHandleBlock, number_of_references_);
@@ -96,6 +100,9 @@
   size_t number_of_references_;
   StackHandleBlock* link_;
 
+  // Fake array, really allocated and filled in by jni_compiler.
+  Object* handles_[0];
+
   DISALLOW_COPY_AND_ASSIGN(StackHandleBlock);
 };
 
@@ -216,13 +223,10 @@
   }
 
   // Number of references allocated in StackHandleBlocks on this thread
-  size_t NumShbHandles() {
-    size_t count = 0;
-    for (StackHandleBlock* cur = top_shb_; cur; cur = cur->Link()) {
-      count += cur->NumberOfReferences();
-    }
-    return count;
-  }
+  size_t NumShbHandles();
+
+  // Is the given obj in this thread's stack handle blocks?
+  bool ShbContains(jobject obj);
 
   // Offset of exception_entry_point_ within Thread, used by generated code
   static ThreadOffset ExceptionEntryPointOffset() {