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

Change-Id: I837eb0ae4af8e314761bb42d3405f05b7a79573e
diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc
index 4fb433f..7d5a447 100644
--- a/src/class_linker_test.cc
+++ b/src/class_linker_test.cc
@@ -14,8 +14,12 @@
  protected:
   void AssertNonExistentClass(const StringPiece& descriptor) {
     EXPECT_TRUE(class_linker_->FindSystemClass(descriptor) == NULL);
-    EXPECT_TRUE(Thread::Current()->IsExceptionPending());
-    Thread::Current()->ClearException();
+    Thread* self = Thread::Current();
+    EXPECT_TRUE(self->IsExceptionPending());
+    Object* exception = self->GetException();
+    self->ClearException();
+    Class* exception_class = class_linker_->FindSystemClass("Ljava/lang/NoClassDefFoundError;");
+    EXPECT_TRUE(exception->InstanceOf(exception_class));
   }
 
   void AssertPrimitiveClass(const StringPiece& descriptor) {
diff --git a/src/common_test.h b/src/common_test.h
index d562226..a405ce5 100644
--- a/src/common_test.h
+++ b/src/common_test.h
@@ -197,6 +197,49 @@
   "ABwBAAAFAAAAAwAAAGwBAAAGAAAAAQAAAIQBAAABIAAAAgAAAKQBAAACIAAAHAAAADwCAAADIAAA"
   "AgAAAOICAAAAIAAAAQAAAPUCAAAAEAAAAQAAABwDAAA=";
 
+//  class AllFields {
+//    static boolean sZ;
+//    static byte sB;
+//    static char sC;
+//    static double sD;
+//    static float sF;
+//    static int sI;
+//    static long sJ;
+//    static short sS;
+//    static Object sObject;
+//    static Object[] sObjectArray;
+//
+//    boolean iZ;
+//    byte iB;
+//    char iC;
+//    double iD;
+//    float iF;
+//    int iI;
+//    long iJ;
+//    short iS;
+//    Object iObject;
+//    Object[] iObjectArray;
+//  }
+static const char kAllFields[] =
+  "ZGV4CjAzNQCdaMDlt1s2Pw65nbVCJcCcZcmroYXvMF/AAwAAcAAAAHhWNBIAAAAAAAAAACwDAAAi"
+  "AAAAcAAAAAwAAAD4AAAAAQAAACgBAAAUAAAANAEAAAIAAADUAQAAAQAAAOQBAAC8AQAABAIAABwC"
+  "AAAkAgAANAIAADcCAAA6AgAAPQIAAEACAABDAgAARgIAAFMCAABnAgAAagIAAG0CAABwAgAAhQIA"
+  "AIkCAACNAgAAkQIAAJUCAACZAgAAnQIAAKYCAAC0AgAAuAIAALwCAADAAgAAxAIAAMgCAADMAgAA"
+  "0AIAANQCAADdAgAA6wIAAO8CAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACQAAAAoAAAAL"
+  "AAAADAAAAA0AAAALAAAACQAAAAAAAAAGAAAADgAAAAYAAQAPAAAABgACABAAAAAGAAMAEQAAAAYA"
+  "BAASAAAABgAFABMAAAAGAAcAFAAAAAYACwAVAAAABgAIABYAAAAGAAoAFwAAAAYAAAAYAAAABgAB"
+  "ABkAAAAGAAIAGgAAAAYAAwAbAAAABgAEABwAAAAGAAUAHQAAAAYABwAeAAAABgALAB8AAAAGAAgA"
+  "IAAAAAYACgAhAAAABgAAAAAAAAAHAAAAAAAAAAYAAAAAAAAABwAAAAAAAAABAAAAAAAAAPgCAAAA"
+  "AAAAAQABAAEAAADzAgAABAAAAHAQAQAAAA4ABjxpbml0PgAOQWxsRmllbGRzLmphdmEAAUIAAUMA"
+  "AUQAAUYAAUkAAUoAC0xBbGxGaWVsZHM7ABJMamF2YS9sYW5nL09iamVjdDsAAVMAAVYAAVoAE1tM"
+  "amF2YS9sYW5nL09iamVjdDsAAmlCAAJpQwACaUQAAmlGAAJpSQACaUoAB2lPYmplY3QADGlPYmpl"
+  "Y3RBcnJheQACaVMAAmlaAAJzQgACc0MAAnNEAAJzRgACc0kAAnNKAAdzT2JqZWN0AAxzT2JqZWN0"
+  "QXJyYXkAAnNTAAJzWgADAAcOAAoKAQAKCAEIAQgBCAEIAQgBCAEIAQgBCAAAAQABAAEAAQABAAEA"
+  "AQABAAEAAICABIQEAAAMAAAAAAAAAAEAAAAAAAAAAQAAACIAAABwAAAAAgAAAAwAAAD4AAAAAwAA"
+  "AAEAAAAoAQAABAAAABQAAAA0AQAABQAAAAIAAADUAQAABgAAAAEAAADkAQAAASAAAAEAAAAEAgAA"
+  "AiAAACIAAAAcAgAAAyAAAAEAAADzAgAAACAAAAEAAAD4AgAAABAAAAEAAAAsAwAA";
+
+
 // class Main {
 //   public static void main(String args[]) {
 //   }
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index a892a28..243a29d 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -582,10 +582,10 @@
     return AddLocalReference<jobject>(ts, field);
   }
 
-  static jclass GetSuperclass(JNIEnv* env, jclass sub) {
+  static jclass GetSuperclass(JNIEnv* env, jclass java_class) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return NULL;
+    Class* c = Decode<Class*>(ts, java_class);
+    return AddLocalReference<jclass>(ts, c->GetSuperClass());
   }
 
   static jboolean IsAssignableFrom(JNIEnv* env, jclass sub, jclass sup) {
@@ -749,10 +749,13 @@
     return 0;
   }
 
-  static jobject AllocObject(JNIEnv* env, jclass clazz) {
+  static jobject AllocObject(JNIEnv* env, jclass java_class) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return NULL;
+    Class* c = Decode<Class*>(ts, java_class);
+    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c)) {
+      return NULL;
+    }
+    return AddLocalReference<jobject>(ts, c->NewInstance());
   }
 
   static jobject NewObject(JNIEnv* env, jclass clazz, jmethodID methodID, ...) {
@@ -764,23 +767,27 @@
     return result;
   }
 
-  static jobject NewObjectV(JNIEnv* env,
-      jclass clazz, jmethodID methodID, va_list args) {
+  static jobject NewObjectV(JNIEnv* env, jclass java_class, jmethodID methodID, va_list args) {
     ScopedJniThreadState ts(env);
-    Class* klass = Decode<Class*>(ts, clazz);
-    Object* result = klass->NewInstance();
+    Class* c = Decode<Class*>(ts, java_class);
+    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c)) {
+      return NULL;
+    }
+    Object* result = c->NewInstance();
     jobject local_result = AddLocalReference<jobject>(ts, result);
-    CallNonvirtualVoidMethodV(env, local_result, clazz, methodID, args);
+    CallNonvirtualVoidMethodV(env, local_result, java_class, methodID, args);
     return local_result;
   }
 
-  static jobject NewObjectA(JNIEnv* env,
-      jclass clazz, jmethodID methodID, jvalue* args) {
+  static jobject NewObjectA(JNIEnv* env, jclass java_class, jmethodID methodID, jvalue* args) {
     ScopedJniThreadState ts(env);
-    Class* klass = Decode<Class*>(ts, clazz);
-    Object* result = klass->NewInstance();
+    Class* c = Decode<Class*>(ts, java_class);
+    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c)) {
+      return NULL;
+    }
+    Object* result = c->NewInstance();
     jobject local_result = AddLocalReference<jobjectArray>(ts, result);
-    CallNonvirtualVoidMethodA(env, local_result, clazz, methodID, args);
+    CallNonvirtualVoidMethodA(env, local_result, java_class, methodID, args);
     return local_result;
   }
 
@@ -1245,103 +1252,172 @@
     return FindFieldID(ts, c, name, sig, true);
   }
 
-  static jobject GetObjectField(JNIEnv* env, jobject obj, jfieldID fieldID) {
+  static jobject GetObjectField(JNIEnv* env, jobject obj, jfieldID fid) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return NULL;
+    Object* o = Decode<Object*>(ts, obj);
+    Field* f = DecodeField(ts, fid);
+    return AddLocalReference<jobject>(ts, f->GetObject(o));
   }
 
-  static jboolean GetBooleanField(JNIEnv* env, jobject obj, jfieldID fieldID) {
+  static jobject GetStaticObjectField(JNIEnv* env, jclass, jfieldID fid) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return JNI_FALSE;
+    Field* f = DecodeField(ts, fid);
+    return AddLocalReference<jobject>(ts, f->GetObject(NULL));
   }
 
-  static jbyte GetByteField(JNIEnv* env, jobject obj, jfieldID fieldID) {
+  static void SetObjectField(JNIEnv* env, jobject java_object, jfieldID fid, jobject java_value) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return 0;
+    Object* o = Decode<Object*>(ts, java_object);
+    Object* v = Decode<Object*>(ts, java_value);
+    Field* f = DecodeField(ts, fid);
+    f->SetObject(o, v);
   }
 
-  static jchar GetCharField(JNIEnv* env, jobject obj, jfieldID fieldID) {
+  static void SetStaticObjectField(JNIEnv* env, jclass, jfieldID fid, jobject java_value) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return 0;
+    Object* v = Decode<Object*>(ts, java_value);
+    Field* f = DecodeField(ts, fid);
+    f->SetObject(NULL, v);
   }
 
-  static jshort GetShortField(JNIEnv* env, jobject obj, jfieldID fieldID) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return 0;
+#define GET_PRIMITIVE_FIELD(fn, instance) \
+  ScopedJniThreadState ts(env); \
+  Object* o = Decode<Object*>(ts, instance); \
+  Field* f = DecodeField(ts, fid); \
+  return f->fn(o)
+
+#define SET_PRIMITIVE_FIELD(fn, instance, value) \
+  ScopedJniThreadState ts(env); \
+  Object* o = Decode<Object*>(ts, instance); \
+  Field* f = DecodeField(ts, fid); \
+  f->fn(o, value)
+
+  static jboolean GetBooleanField(JNIEnv* env, jobject obj, jfieldID fid) {
+    GET_PRIMITIVE_FIELD(GetBoolean, obj);
   }
 
-  static jint GetIntField(JNIEnv* env, jobject obj, jfieldID fieldID) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return 0;
+  static jbyte GetByteField(JNIEnv* env, jobject obj, jfieldID fid) {
+    GET_PRIMITIVE_FIELD(GetByte, obj);
   }
 
-  static jlong GetLongField(JNIEnv* env, jobject obj, jfieldID fieldID) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return 0;
+  static jchar GetCharField(JNIEnv* env, jobject obj, jfieldID fid) {
+    GET_PRIMITIVE_FIELD(GetChar, obj);
   }
 
-  static jfloat GetFloatField(JNIEnv* env, jobject obj, jfieldID fieldID) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return 0;
+  static jshort GetShortField(JNIEnv* env, jobject obj, jfieldID fid) {
+    GET_PRIMITIVE_FIELD(GetShort, obj);
   }
 
-  static jdouble GetDoubleField(JNIEnv* env, jobject obj, jfieldID fieldID) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return 0;
+  static jint GetIntField(JNIEnv* env, jobject obj, jfieldID fid) {
+    GET_PRIMITIVE_FIELD(GetInt, obj);
   }
 
-  static void SetObjectField(JNIEnv* env, jobject obj, jfieldID fieldID, jobject val) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+  static jlong GetLongField(JNIEnv* env, jobject obj, jfieldID fid) {
+    GET_PRIMITIVE_FIELD(GetLong, obj);
   }
 
-  static void SetBooleanField(JNIEnv* env, jobject obj, jfieldID fieldID, jboolean val) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+  static jfloat GetFloatField(JNIEnv* env, jobject obj, jfieldID fid) {
+    GET_PRIMITIVE_FIELD(GetFloat, obj);
   }
 
-  static void SetByteField(JNIEnv* env, jobject obj, jfieldID fieldID, jbyte val) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+  static jdouble GetDoubleField(JNIEnv* env, jobject obj, jfieldID fid) {
+    GET_PRIMITIVE_FIELD(GetDouble, obj);
   }
 
-  static void SetCharField(JNIEnv* env, jobject obj, jfieldID fieldID, jchar val) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+  static jboolean GetStaticBooleanField(JNIEnv* env, jclass clazz, jfieldID fid) {
+    GET_PRIMITIVE_FIELD(GetBoolean, NULL);
   }
 
-  static void SetShortField(JNIEnv* env, jobject obj, jfieldID fieldID, jshort val) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+  static jbyte GetStaticByteField(JNIEnv* env, jclass clazz, jfieldID fid) {
+    GET_PRIMITIVE_FIELD(GetByte, NULL);
   }
 
-  static void SetIntField(JNIEnv* env, jobject obj, jfieldID fieldID, jint val) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+  static jchar GetStaticCharField(JNIEnv* env, jclass clazz, jfieldID fid) {
+    GET_PRIMITIVE_FIELD(GetChar, NULL);
   }
 
-  static void SetLongField(JNIEnv* env, jobject obj, jfieldID fieldID, jlong val) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+  static jshort GetStaticShortField(JNIEnv* env, jclass clazz, jfieldID fid) {
+    GET_PRIMITIVE_FIELD(GetShort, NULL);
   }
 
-  static void SetFloatField(JNIEnv* env, jobject obj, jfieldID fieldID, jfloat val) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+  static jint GetStaticIntField(JNIEnv* env, jclass clazz, jfieldID fid) {
+    GET_PRIMITIVE_FIELD(GetInt, NULL);
   }
 
-  static void SetDoubleField(JNIEnv* env, jobject obj, jfieldID fieldID, jdouble val) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
+  static jlong GetStaticLongField(JNIEnv* env, jclass clazz, jfieldID fid) {
+    GET_PRIMITIVE_FIELD(GetLong, NULL);
+  }
+
+  static jfloat GetStaticFloatField(JNIEnv* env, jclass clazz, jfieldID fid) {
+    GET_PRIMITIVE_FIELD(GetFloat, NULL);
+  }
+
+  static jdouble GetStaticDoubleField(JNIEnv* env, jclass clazz, jfieldID fid) {
+    GET_PRIMITIVE_FIELD(GetDouble, NULL);
+  }
+
+  static void SetBooleanField(JNIEnv* env, jobject obj, jfieldID fid, jboolean v) {
+    SET_PRIMITIVE_FIELD(SetBoolean, obj, v);
+  }
+
+  static void SetByteField(JNIEnv* env, jobject obj, jfieldID fid, jbyte v) {
+    SET_PRIMITIVE_FIELD(SetByte, obj, v);
+  }
+
+  static void SetCharField(JNIEnv* env, jobject obj, jfieldID fid, jchar v) {
+    SET_PRIMITIVE_FIELD(SetChar, obj, v);
+  }
+
+  static void SetFloatField(JNIEnv* env, jobject obj, jfieldID fid, jfloat v) {
+    SET_PRIMITIVE_FIELD(SetFloat, obj, v);
+  }
+
+  static void SetDoubleField(JNIEnv* env, jobject obj, jfieldID fid, jdouble v) {
+    SET_PRIMITIVE_FIELD(SetDouble, obj, v);
+  }
+
+  static void SetIntField(JNIEnv* env, jobject obj, jfieldID fid, jint v) {
+    SET_PRIMITIVE_FIELD(SetInt, obj, v);
+  }
+
+  static void SetLongField(JNIEnv* env, jobject obj, jfieldID fid, jlong v) {
+    SET_PRIMITIVE_FIELD(SetLong, obj, v);
+  }
+
+  static void SetShortField(JNIEnv* env, jobject obj, jfieldID fid, jshort v) {
+    SET_PRIMITIVE_FIELD(SetShort, obj, v);
+  }
+
+  static void SetStaticBooleanField(JNIEnv* env, jclass, jfieldID fid, jboolean v) {
+    SET_PRIMITIVE_FIELD(SetBoolean, NULL, v);
+  }
+
+  static void SetStaticByteField(JNIEnv* env, jclass, jfieldID fid, jbyte v) {
+    SET_PRIMITIVE_FIELD(SetByte, NULL, v);
+  }
+
+  static void SetStaticCharField(JNIEnv* env, jclass, jfieldID fid, jchar v) {
+    SET_PRIMITIVE_FIELD(SetChar, NULL, v);
+  }
+
+  static void SetStaticFloatField(JNIEnv* env, jclass, jfieldID fid, jfloat v) {
+    SET_PRIMITIVE_FIELD(SetFloat, NULL, v);
+  }
+
+  static void SetStaticDoubleField(JNIEnv* env, jclass, jfieldID fid, jdouble v) {
+    SET_PRIMITIVE_FIELD(SetDouble, NULL, v);
+  }
+
+  static void SetStaticIntField(JNIEnv* env, jclass, jfieldID fid, jint v) {
+    SET_PRIMITIVE_FIELD(SetInt, NULL, v);
+  }
+
+  static void SetStaticLongField(JNIEnv* env, jclass, jfieldID fid, jlong v) {
+    SET_PRIMITIVE_FIELD(SetLong, NULL, v);
+  }
+
+  static void SetStaticShortField(JNIEnv* env, jclass, jfieldID fid, jshort v) {
+    SET_PRIMITIVE_FIELD(SetShort, NULL, v);
   }
 
   static jobject CallStaticObjectMethod(JNIEnv* env,
@@ -1558,114 +1634,6 @@
     InvokeWithJValues(ts, NULL, methodID, args);
   }
 
-  static jobject GetStaticObjectField(JNIEnv* env, jclass clazz, jfieldID fieldID) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return NULL;
-  }
-
-  static jboolean GetStaticBooleanField(JNIEnv* env, jclass clazz, jfieldID fieldID) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return JNI_FALSE;
-  }
-
-  static jbyte GetStaticByteField(JNIEnv* env, jclass clazz, jfieldID fieldID) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return 0;
-  }
-
-  static jchar GetStaticCharField(JNIEnv* env, jclass clazz, jfieldID fieldID) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return 0;
-  }
-
-  static jshort GetStaticShortField(JNIEnv* env, jclass clazz, jfieldID fieldID) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return 0;
-  }
-
-  static jint GetStaticIntField(JNIEnv* env, jclass clazz, jfieldID fieldID) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return 0;
-  }
-
-  static jlong GetStaticLongField(JNIEnv* env, jclass clazz, jfieldID fieldID) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return 0;
-  }
-
-  static jfloat GetStaticFloatField(JNIEnv* env, jclass clazz, jfieldID fieldID) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return 0;
-  }
-
-  static jdouble GetStaticDoubleField(JNIEnv* env, jclass clazz, jfieldID fieldID) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return 0;
-  }
-
-  static void SetStaticObjectField(JNIEnv* env,
-      jclass clazz, jfieldID fieldID, jobject value) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-  }
-
-  static void SetStaticBooleanField(JNIEnv* env,
-      jclass clazz, jfieldID fieldID, jboolean value) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-  }
-
-  static void SetStaticByteField(JNIEnv* env,
-      jclass clazz, jfieldID fieldID, jbyte value) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-  }
-
-  static void SetStaticCharField(JNIEnv* env,
-      jclass clazz, jfieldID fieldID, jchar value) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-  }
-
-  static void SetStaticShortField(JNIEnv* env,
-      jclass clazz, jfieldID fieldID, jshort value) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-  }
-
-  static void SetStaticIntField(JNIEnv* env,
-      jclass clazz, jfieldID fieldID, jint value) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-  }
-
-  static void SetStaticLongField(JNIEnv* env,
-      jclass clazz, jfieldID fieldID, jlong value) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-  }
-
-  static void SetStaticFloatField(JNIEnv* env,
-      jclass clazz, jfieldID fieldID, jfloat value) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-  }
-
-  static void SetStaticDoubleField(JNIEnv* env,
-      jclass clazz, jfieldID fieldID, jdouble value) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-  }
-
   static jstring NewString(JNIEnv* env, const jchar* unicode, jsize len) {
     ScopedJniThreadState ts(env);
     UNIMPLEMENTED(FATAL);
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);
 }
diff --git a/src/object.cc b/src/object.cc
index 9316660..6a349ac 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -226,7 +226,6 @@
 
 void Field::SetByte(Object* object, int8_t b) const {
   CHECK_EQ(GetType(), 'B');
-  CHECK(IsStatic());
   Set32(object, b);
 }
 
@@ -237,7 +236,6 @@
 
 void Field::SetChar(Object* object, uint16_t c) const {
   CHECK_EQ(GetType(), 'C');
-  CHECK(IsStatic());
   Set32(object, c);
 }
 
@@ -248,7 +246,6 @@
 
 void Field::SetShort(Object* object, uint16_t s) const {
   CHECK_EQ(GetType(), 'S');
-  CHECK(IsStatic());
   Set32(object, s);
 }
 
@@ -259,7 +256,6 @@
 
 void Field::SetInt(Object* object, int32_t i) const {
   CHECK_EQ(GetType(), 'I');
-  CHECK(IsStatic());
   Set32(object, i);
 }
 
@@ -270,7 +266,6 @@
 
 void Field::SetLong(Object* object, int64_t j) const {
   CHECK_EQ(GetType(), 'J');
-  CHECK(IsStatic());
   Set64(object, j);
 }
 
@@ -283,7 +278,6 @@
 
 void Field::SetFloat(Object* object, float f) const {
   CHECK_EQ(GetType(), 'F');
-  CHECK(IsStatic());
   JValue float_bits;
   float_bits.f = f;
   Set32(object, float_bits.i);
@@ -298,7 +292,6 @@
 
 void Field::SetDouble(Object* object, double d) const {
   CHECK_EQ(GetType(), 'D');
-  CHECK(IsStatic());
   JValue double_bits;
   double_bits.d = d;
   Set64(object, double_bits.j);