Implement jfieldID and jmethodID properly.

Change-Id: I048107fbca4e21cf34e8fda6defdbc0b97421cf0
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index f51b6c9..6c5d6f2 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -57,23 +57,91 @@
   EXPECT_CLASS_NOT_FOUND("K");
 }
 
+#define EXPECT_EXCEPTION(exception_class) \
+  do { \
+    EXPECT_TRUE(env_->ExceptionCheck()); \
+    jthrowable exception = env_->ExceptionOccurred(); \
+    EXPECT_NE(static_cast<jthrowable>(NULL), exception); \
+    EXPECT_TRUE(env_->IsInstanceOf(exception, exception_class)); \
+    env_->ExceptionClear(); \
+  } while (false)
+
+TEST_F(JniInternalTest, GetFieldID) {
+  jclass jlnsfe = env_->FindClass("java/lang/NoSuchFieldError");
+  ASSERT_TRUE(jlnsfe != NULL);
+  jclass c = env_->FindClass("java/lang/String");
+  ASSERT_TRUE(c != NULL);
+
+  // Wrong type.
+  jfieldID fid = env_->GetFieldID(c, "count", "J");
+  EXPECT_EQ(static_cast<jfieldID>(NULL), fid);
+  EXPECT_EXCEPTION(jlnsfe);
+
+  // Wrong name.
+  fid = env_->GetFieldID(c, "Count", "I");
+  EXPECT_EQ(static_cast<jfieldID>(NULL), fid);
+  EXPECT_EXCEPTION(jlnsfe);
+
+  // Good declared field lookup.
+  fid = env_->GetFieldID(c, "count", "I");
+  EXPECT_NE(static_cast<jfieldID>(NULL), fid);
+  EXPECT_TRUE(fid != NULL);
+  EXPECT_FALSE(env_->ExceptionCheck());
+
+  // Good superclass field lookup.
+  c = env_->FindClass("java/lang/StringBuilder");
+  fid = env_->GetFieldID(c, "count", "I");
+  EXPECT_NE(static_cast<jfieldID>(NULL), fid);
+  EXPECT_TRUE(fid != NULL);
+  EXPECT_FALSE(env_->ExceptionCheck());
+
+  // Not instance.
+  fid = env_->GetFieldID(c, "CASE_INSENSITIVE_ORDER", "Ljava/util/Comparator;");
+  EXPECT_EQ(static_cast<jfieldID>(NULL), fid);
+  EXPECT_EXCEPTION(jlnsfe);
+}
+
+TEST_F(JniInternalTest, GetStaticFieldID) {
+  jclass jlnsfe = env_->FindClass("java/lang/NoSuchFieldError");
+  ASSERT_TRUE(jlnsfe != NULL);
+  jclass c = env_->FindClass("java/lang/String");
+  ASSERT_TRUE(c != NULL);
+
+  // Wrong type.
+  jfieldID fid = env_->GetStaticFieldID(c, "CASE_INSENSITIVE_ORDER", "J");
+  EXPECT_EQ(static_cast<jfieldID>(NULL), fid);
+  EXPECT_EXCEPTION(jlnsfe);
+
+  // Wrong name.
+  fid = env_->GetStaticFieldID(c, "cASE_INSENSITIVE_ORDER", "Ljava/util/Comparator;");
+  EXPECT_EQ(static_cast<jfieldID>(NULL), fid);
+  EXPECT_EXCEPTION(jlnsfe);
+
+  // Good declared field lookup.
+  fid = env_->GetStaticFieldID(c, "CASE_INSENSITIVE_ORDER", "Ljava/util/Comparator;");
+  EXPECT_NE(static_cast<jfieldID>(NULL), fid);
+  EXPECT_TRUE(fid != NULL);
+  EXPECT_FALSE(env_->ExceptionCheck());
+
+  // Not static.
+  fid = env_->GetStaticFieldID(c, "count", "I");
+  EXPECT_EQ(static_cast<jfieldID>(NULL), fid);
+  EXPECT_EXCEPTION(jlnsfe);
+}
+
 TEST_F(JniInternalTest, GetMethodID) {
   jclass jlobject = env_->FindClass("java/lang/Object");
   jclass jlstring = env_->FindClass("java/lang/String");
   jclass jlnsme = env_->FindClass("java/lang/NoSuchMethodError");
 
   // Sanity check that no exceptions are pending
-  EXPECT_FALSE(env_->ExceptionCheck());
+  ASSERT_FALSE(env_->ExceptionCheck());
 
   // Check that java.lang.Object.foo() doesn't exist and NoSuchMethodError is
   // a pending exception
   jmethodID method = env_->GetMethodID(jlobject, "foo", "()V");
   EXPECT_EQ(static_cast<jmethodID>(NULL), method);
-  EXPECT_TRUE(env_->ExceptionCheck());
-  jthrowable exception = env_->ExceptionOccurred();
-  EXPECT_NE(static_cast<jthrowable>(NULL), exception);
-  EXPECT_TRUE(env_->IsInstanceOf(exception, jlnsme));
-  env_->ExceptionClear();
+  EXPECT_EXCEPTION(jlnsme);
 
   // Check that java.lang.Object.equals() does exist
   method = env_->GetMethodID(jlobject, "equals", "(Ljava/lang/Object;)Z");
@@ -84,11 +152,7 @@
   // method is static
   method = env_->GetMethodID(jlstring, "valueOf", "(I)Ljava/lang/String;");
   EXPECT_EQ(static_cast<jmethodID>(NULL), method);
-  EXPECT_TRUE(env_->ExceptionCheck());
-  exception = env_->ExceptionOccurred();
-  EXPECT_NE(static_cast<jthrowable>(NULL), exception);
-  EXPECT_TRUE(env_->IsInstanceOf(exception, jlnsme));
-  env_->ExceptionClear();
+  EXPECT_EXCEPTION(jlnsme);
 }
 
 TEST_F(JniInternalTest, GetStaticMethodID) {
@@ -96,27 +160,19 @@
   jclass jlnsme = env_->FindClass("java/lang/NoSuchMethodError");
 
   // Sanity check that no exceptions are pending
-  EXPECT_FALSE(env_->ExceptionCheck());
+  ASSERT_FALSE(env_->ExceptionCheck());
 
   // Check that java.lang.Object.foo() doesn't exist and NoSuchMethodError is
   // a pending exception
   jmethodID method = env_->GetStaticMethodID(jlobject, "foo", "()V");
   EXPECT_EQ(static_cast<jmethodID>(NULL), method);
-  EXPECT_TRUE(env_->ExceptionCheck());
-  jthrowable exception = env_->ExceptionOccurred();
-  EXPECT_NE(static_cast<jthrowable>(NULL), exception);
-  EXPECT_TRUE(env_->IsInstanceOf(exception, jlnsme));
-  env_->ExceptionClear();
+  EXPECT_EXCEPTION(jlnsme);
 
   // Check that GetStaticMethodID for java.lang.Object.equals(Object) fails as
   // the method is not static
   method = env_->GetStaticMethodID(jlobject, "equals", "(Ljava/lang/Object;)Z");
   EXPECT_EQ(static_cast<jmethodID>(NULL), method);
-  EXPECT_TRUE(env_->ExceptionCheck());
-  exception = env_->ExceptionOccurred();
-  EXPECT_NE(static_cast<jthrowable>(NULL), exception);
-  EXPECT_TRUE(env_->IsInstanceOf(exception, jlnsme));
-  env_->ExceptionClear();
+  EXPECT_EXCEPTION(jlnsme);
 
   // Check that java.lang.String.valueOf(int) does exist
   jclass jlstring = env_->FindClass("java/lang/String");
@@ -126,12 +182,42 @@
   EXPECT_FALSE(env_->ExceptionCheck());
 }
 
+TEST_F(JniInternalTest, FromReflectedField_ToReflectedField) {
+  jclass jlrField = env_->FindClass("java/lang/reflect/Field");
+  jclass c = env_->FindClass("java/lang/String");
+  ASSERT_TRUE(c != NULL);
+  jfieldID fid = env_->GetFieldID(c, "count", "I");
+  ASSERT_TRUE(fid != NULL);
+  // Turn the fid into a java.lang.reflect.Field...
+  jobject field = env_->ToReflectedField(c, fid, JNI_FALSE);
+  ASSERT_TRUE(c != NULL);
+  ASSERT_TRUE(env_->IsInstanceOf(field, jlrField));
+  // ...and back again.
+  jfieldID fid2 = env_->FromReflectedField(field);
+  ASSERT_TRUE(fid2 != NULL);
+}
+
+TEST_F(JniInternalTest, FromReflectedMethod_ToReflectedMethod) {
+  jclass jlrMethod = env_->FindClass("java/lang/reflect/Method");
+  jclass c = env_->FindClass("java/lang/String");
+  ASSERT_TRUE(c != NULL);
+  jmethodID mid = env_->GetMethodID(c, "length", "()I");
+  ASSERT_TRUE(mid != NULL);
+  // Turn the mid into a java.lang.reflect.Method...
+  jobject method = env_->ToReflectedMethod(c, mid, JNI_FALSE);
+  ASSERT_TRUE(c != NULL);
+  ASSERT_TRUE(env_->IsInstanceOf(method, jlrMethod));
+  // ...and back again.
+  jmethodID mid2 = env_->FromReflectedMethod(method);
+  ASSERT_TRUE(mid2 != NULL);
+}
+
 TEST_F(JniInternalTest, RegisterNatives) {
   jclass jlobject = env_->FindClass("java/lang/Object");
   jclass jlnsme = env_->FindClass("java/lang/NoSuchMethodError");
 
   // Sanity check that no exceptions are pending
-  EXPECT_FALSE(env_->ExceptionCheck());
+  ASSERT_FALSE(env_->ExceptionCheck());
 
   // Check that registering to a non-existent java.lang.Object.foo() causes a
   // NoSuchMethodError
@@ -139,22 +225,14 @@
     JNINativeMethod methods[] = {{"foo", "()V", NULL}};
     env_->RegisterNatives(jlobject, methods, 1);
   }
-  EXPECT_TRUE(env_->ExceptionCheck());
-  jthrowable exception = env_->ExceptionOccurred();
-  EXPECT_NE(static_cast<jthrowable>(NULL), exception);
-  EXPECT_TRUE(env_->IsInstanceOf(exception, jlnsme));
-  env_->ExceptionClear();
+  EXPECT_EXCEPTION(jlnsme);
 
   // Check that registering non-native methods causes a NoSuchMethodError
   {
     JNINativeMethod methods[] = {{"equals", "(Ljava/lang/Object;)Z", NULL}};
     env_->RegisterNatives(jlobject, methods, 1);
   }
-  EXPECT_TRUE(env_->ExceptionCheck());
-  exception = env_->ExceptionOccurred();
-  EXPECT_NE(static_cast<jthrowable>(NULL), exception);
-  EXPECT_TRUE(env_->IsInstanceOf(exception, jlnsme));
-  env_->ExceptionClear();
+  EXPECT_EXCEPTION(jlnsme);
 
   // Check that registering native methods is successful
   {
@@ -215,6 +293,7 @@
 }
 
 TEST_F(JniInternalTest, SetObjectArrayElement) {
+  jclass aioobe = env_->FindClass("java/lang/ArrayIndexOutOfBoundsException");
   jclass c = env_->FindClass("[Ljava/lang/Object;");
   ASSERT_TRUE(c != NULL);
 
@@ -224,16 +303,12 @@
   // TODO: check reading value back
 
   // ArrayIndexOutOfBounds for negative index.
-  // TODO: check exception type
   env_->SetObjectArrayElement(array, -1, c);
-  EXPECT_TRUE(env_->ExceptionCheck());
-  env_->ExceptionClear();
+  EXPECT_EXCEPTION(aioobe);
 
   // ArrayIndexOutOfBounds for too-large index.
-  // TODO: check exception type
   env_->SetObjectArrayElement(array, 1, c);
-  EXPECT_TRUE(env_->ExceptionCheck());
-  env_->ExceptionClear();
+  EXPECT_EXCEPTION(aioobe);
 
   // TODO: check ArrayStoreException thrown for bad types.
 }