Implement EnsureCapacity, PushLocalFrame, and PopLocalFrame.

These are as good as the old implementations, except that unbalanced usages
won't be cleaned up completely (you'll slowly grow the vector in your JNIEnv).

This patch also renames IndirectReferenceTable::Contains to the less misleading
ContainsDirectPointer, and fixes JNI::GetObjectRefType to not claim that
invalid local references are locals indefinitely.

We also now include detail messages in OOMEs where possible. (Test 061 still
passes.) We still log regardless, since OOME should be a rare thing.

Change-Id: I77b2f44ea066e92c517e5c96700ec533727b9c78
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index ddd39b5..59dca3a 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -720,7 +720,7 @@
   EXPECT_TRUE(o != NULL);
   EXPECT_TRUE(o != s);
 
-  // TODO: check that o is a local reference.
+  EXPECT_EQ(JNILocalRefType, env_->GetObjectRefType(o));
 }
 
 TEST_F(JniInternalTest, DeleteLocalRef_NULL) {
@@ -746,6 +746,43 @@
   env_->DeleteLocalRef(o);
 }
 
+TEST_F(JniInternalTest, PushLocalFrame_PopLocalFrame) {
+  jobject original = env_->NewStringUTF("");
+  ASSERT_TRUE(original != NULL);
+
+  jobject outer;
+  jobject inner1, inner2;
+  Object* inner2_direct_pointer;
+  {
+    env_->PushLocalFrame(4);
+    outer = env_->NewLocalRef(original);
+
+    {
+      env_->PushLocalFrame(4);
+      inner1 = env_->NewLocalRef(outer);
+      inner2 = env_->NewStringUTF("survivor");
+      inner2_direct_pointer = Decode<Object*>(env_, inner2);
+      env_->PopLocalFrame(inner2);
+    }
+
+    EXPECT_EQ(JNILocalRefType, env_->GetObjectRefType(original));
+    EXPECT_EQ(JNILocalRefType, env_->GetObjectRefType(outer));
+    EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner1));
+
+    // Our local reference for the survivor is invalid because the survivor
+    // gets a new local reference...
+    EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner2));
+    // ...but the survivor should be in the local reference table.
+    EXPECT_TRUE(env_->locals.ContainsDirectPointer(inner2_direct_pointer));
+
+    env_->PopLocalFrame(NULL);
+  }
+  EXPECT_EQ(JNILocalRefType, env_->GetObjectRefType(original));
+  EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(outer));
+  EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner1));
+  EXPECT_EQ(JNIInvalidRefType, env_->GetObjectRefType(inner2));
+}
+
 TEST_F(JniInternalTest, NewGlobalRef_NULL) {
   EXPECT_TRUE(env_->NewGlobalRef(NULL) == NULL);
 }