Implement field access (and a few other bits and bobs).

Change-Id: I837eb0ae4af8e314761bb42d3405f05b7a79573e
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index 9043bcd..62f6068 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -19,6 +19,21 @@
   JNIEnv* env_;
 };
 
+TEST_F(JniInternalTest, AllocObject) {
+  jclass c = env_->FindClass("java/lang/String");
+  ASSERT_TRUE(c != NULL);
+  jobject o = env_->AllocObject(c);
+  ASSERT_TRUE(o != NULL);
+
+  // We have an instance of the class we asked for...
+  ASSERT_TRUE(env_->IsInstanceOf(o, c));
+  // ...whose fields haven't been initialized because
+  // we didn't call a constructor.
+  ASSERT_EQ(0, env_->GetIntField(o, env_->GetFieldID(c, "count", "I")));
+  ASSERT_EQ(0, env_->GetIntField(o, env_->GetFieldID(c, "offset", "I")));
+  ASSERT_TRUE(env_->GetObjectField(o, env_->GetFieldID(c, "value", "[C")) == NULL);
+}
+
 TEST_F(JniInternalTest, GetVersion) {
   ASSERT_EQ(JNI_VERSION_1_6, env_->GetVersion());
 }
@@ -303,6 +318,15 @@
   // Already tested in NewObjectArray/NewPrimitiveArray.
 }
 
+TEST_F(JniInternalTest, GetSuperclass) {
+  jclass object_class = env_->FindClass("java/lang/Object");
+  ASSERT_TRUE(object_class != NULL);
+  jclass string_class = env_->FindClass("java/lang/String");
+  ASSERT_TRUE(string_class != NULL);
+  ASSERT_TRUE(env_->IsSameObject(object_class, env_->GetSuperclass(string_class)));
+  ASSERT_TRUE(env_->GetSuperclass(object_class) == NULL);
+}
+
 TEST_F(JniInternalTest, NewStringUTF) {
   EXPECT_TRUE(env_->NewStringUTF(NULL) == NULL);
   EXPECT_TRUE(env_->NewStringUTF("") != NULL);
@@ -331,6 +355,87 @@
   // TODO: check ArrayStoreException thrown for bad types.
 }
 
+#define EXPECT_STATIC_PRIMITIVE_FIELD(type, field_name, sig, value1, value2) \
+  do { \
+    jfieldID fid = env_->GetStaticFieldID(c, field_name, sig); \
+    EXPECT_TRUE(fid != NULL); \
+    env_->SetStatic ## type ## Field(c, fid, value1); \
+    EXPECT_EQ(value1, env_->GetStatic ## type ## Field(c, fid)); \
+    env_->SetStatic ## type ## Field(c, fid, value2); \
+    EXPECT_EQ(value2, env_->GetStatic ## type ## Field(c, fid)); \
+  } while (false)
+
+#define EXPECT_PRIMITIVE_FIELD(instance, type, field_name, sig, value1, value2) \
+  do { \
+    jfieldID fid = env_->GetFieldID(c, field_name, sig); \
+    EXPECT_TRUE(fid != NULL); \
+    env_->Set ## type ## Field(instance, fid, value1); \
+    EXPECT_EQ(value1, env_->Get ## type ## Field(instance, fid)); \
+    env_->Set ## type ## Field(instance, fid, value2); \
+    EXPECT_EQ(value2, env_->Get ## type ## Field(instance, fid)); \
+  } while (false)
+
+
+TEST_F(JniInternalTest, GetPrimitiveField_SetPrimitiveField) {
+  scoped_ptr<DexFile> dex(OpenDexFileBase64(kAllFields, "kAllFields"));
+  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
+  Thread::Current()->SetClassLoaderOverride(class_loader);
+
+  jclass c = env_->FindClass("AllFields");
+  ASSERT_TRUE(c != NULL);
+  jobject o = env_->AllocObject(c);
+  ASSERT_TRUE(o != NULL);
+
+  EXPECT_STATIC_PRIMITIVE_FIELD(Boolean, "sZ", "Z", true, false);
+  EXPECT_STATIC_PRIMITIVE_FIELD(Byte, "sB", "B", 1, 2);
+  EXPECT_STATIC_PRIMITIVE_FIELD(Char, "sC", "C", 'a', 'b');
+  EXPECT_STATIC_PRIMITIVE_FIELD(Double, "sD", "D", 1.0, 2.0);
+  EXPECT_STATIC_PRIMITIVE_FIELD(Float, "sF", "F", 1.0, 2.0);
+  EXPECT_STATIC_PRIMITIVE_FIELD(Int, "sI", "I", 1, 2);
+  EXPECT_STATIC_PRIMITIVE_FIELD(Long, "sJ", "J", 1, 2);
+  EXPECT_STATIC_PRIMITIVE_FIELD(Short, "sS", "S", 1, 2);
+
+  EXPECT_PRIMITIVE_FIELD(o, Boolean, "iZ", "Z", true, false);
+  EXPECT_PRIMITIVE_FIELD(o, Byte, "iB", "B", 1, 2);
+  EXPECT_PRIMITIVE_FIELD(o, Char, "iC", "C", 'a', 'b');
+  EXPECT_PRIMITIVE_FIELD(o, Double, "iD", "D", 1.0, 2.0);
+  EXPECT_PRIMITIVE_FIELD(o, Float, "iF", "F", 1.0, 2.0);
+  EXPECT_PRIMITIVE_FIELD(o, Int, "iI", "I", 1, 2);
+  EXPECT_PRIMITIVE_FIELD(o, Long, "iJ", "J", 1, 2);
+  EXPECT_PRIMITIVE_FIELD(o, Short, "iS", "S", 1, 2);
+}
+
+TEST_F(JniInternalTest, GetObjectField_SetObjectField) {
+  scoped_ptr<DexFile> dex(OpenDexFileBase64(kAllFields, "kAllFields"));
+  PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
+  Thread::Current()->SetClassLoaderOverride(class_loader);
+
+  jclass c = env_->FindClass("AllFields");
+  ASSERT_TRUE(c != NULL);
+  jobject o = env_->AllocObject(c);
+  ASSERT_TRUE(o != NULL);
+
+  jstring s1 = env_->NewStringUTF("hello");
+  ASSERT_TRUE(s1 != NULL);
+  jstring s2 = env_->NewStringUTF("world");
+  ASSERT_TRUE(s2 != NULL);
+
+  jfieldID s_fid = env_->GetStaticFieldID(c, "sObject", "Ljava/lang/Object;");
+  ASSERT_TRUE(s_fid != NULL);
+  jfieldID i_fid = env_->GetFieldID(c, "iObject", "Ljava/lang/Object;");
+  ASSERT_TRUE(i_fid != NULL);
+
+  env_->SetStaticObjectField(c, s_fid, s1);
+  ASSERT_TRUE(env_->IsSameObject(s1, env_->GetStaticObjectField(c, s_fid)));
+  env_->SetStaticObjectField(c, s_fid, s2);
+  ASSERT_TRUE(env_->IsSameObject(s2, env_->GetStaticObjectField(c, s_fid)));
+
+  env_->SetObjectField(o, i_fid, s1);
+  ASSERT_TRUE(env_->IsSameObject(s1, env_->GetObjectField(o, i_fid)));
+  env_->SetObjectField(o, i_fid, s2);
+  ASSERT_TRUE(env_->IsSameObject(s2, env_->GetObjectField(o, i_fid)));
+}
+
 TEST_F(JniInternalTest, NewLocalRef_NULL) {
   EXPECT_TRUE(env_->NewLocalRef(NULL) == NULL);
 }