Implement more of the exception/object/class JNI functions.

Change-Id: Id835c1a37e5034d11e2fc43ccf49e578510abfc1
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 243a29d..9aa23bb 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -582,28 +582,68 @@
     return AddLocalReference<jobject>(ts, field);
   }
 
+  static jclass GetObjectClass(JNIEnv* env, jobject java_object) {
+    ScopedJniThreadState ts(env);
+    Object* o = Decode<Object*>(ts, java_object);
+    return AddLocalReference<jclass>(ts, o->GetClass());
+  }
+
   static jclass GetSuperclass(JNIEnv* env, jclass java_class) {
     ScopedJniThreadState ts(env);
     Class* c = Decode<Class*>(ts, java_class);
     return AddLocalReference<jclass>(ts, c->GetSuperClass());
   }
 
-  static jboolean IsAssignableFrom(JNIEnv* env, jclass sub, jclass sup) {
+  static jboolean IsAssignableFrom(JNIEnv* env, jclass java_class1, jclass java_class2) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return JNI_FALSE;
+    Class* c1 = Decode<Class*>(ts, java_class1);
+    Class* c2 = Decode<Class*>(ts, java_class2);
+    return c1->IsAssignableFrom(c2) ? JNI_TRUE : JNI_FALSE;
   }
 
-  static jint Throw(JNIEnv* env, jthrowable obj) {
+  static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass clazz) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return 0;
+    CHECK_NE(static_cast<jclass>(NULL), clazz);
+    if (jobj == NULL) {
+      // NB. JNI is different from regular Java instanceof in this respect
+      return JNI_TRUE;
+    } else {
+      Object* obj = Decode<Object*>(ts, jobj);
+      Class* klass = Decode<Class*>(ts, clazz);
+      return Object::InstanceOf(obj, klass) ? JNI_TRUE : JNI_FALSE;
+    }
   }
 
-  static jint ThrowNew(JNIEnv* env, jclass clazz, const char* msg) {
+  static jint Throw(JNIEnv* env, jthrowable java_exception) {
+    ScopedJniThreadState ts(env);
+    Object* exception = Decode<Object*>(ts, java_exception);
+    if (exception == NULL) {
+      return JNI_ERR;
+    }
+    ts.Self()->SetException(exception);
+    return JNI_OK;
+  }
+
+  static jint ThrowNew(JNIEnv* env, jclass java_class, const char* msg) {
+    ScopedJniThreadState ts(env);
+    Class* c = Decode<Class*>(ts, java_class);
+    ts.Self()->ThrowNewException(c, msg);
+    return JNI_OK;
+  }
+
+  static jboolean ExceptionCheck(JNIEnv* env) {
+    ScopedJniThreadState ts(env);
+    return ts.Self()->IsExceptionPending() ? JNI_TRUE : JNI_FALSE;
+  }
+
+  static void ExceptionClear(JNIEnv* env) {
+    ScopedJniThreadState ts(env);
+    ts.Self()->ClearException();
+  }
+
+  static void ExceptionDescribe(JNIEnv* env) {
     ScopedJniThreadState ts(env);
     UNIMPLEMENTED(FATAL);
-    return 0;
   }
 
   static jthrowable ExceptionOccurred(JNIEnv* env) {
@@ -627,16 +667,6 @@
     }
   }
 
-  static void ExceptionDescribe(JNIEnv* env) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-  }
-
-  static void ExceptionClear(JNIEnv* env) {
-    ScopedJniThreadState ts(env);
-    ts.Self()->ClearException();
-  }
-
   static void FatalError(JNIEnv* env, const char* msg) {
     ScopedJniThreadState ts(env);
     LOG(FATAL) << "JNI FatalError called: " << msg;
@@ -791,25 +821,6 @@
     return local_result;
   }
 
-  static jclass GetObjectClass(JNIEnv* env, jobject obj) {
-    ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return NULL;
-  }
-
-  static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass clazz) {
-    ScopedJniThreadState ts(env);
-    CHECK_NE(static_cast<jclass>(NULL), clazz);
-    if (jobj == NULL) {
-      // NB. JNI is different from regular Java instanceof in this respect
-      return JNI_TRUE;
-    } else {
-      Object* obj = Decode<Object*>(ts, jobj);
-      Class* klass = Decode<Class*>(ts, clazz);
-      return Object::InstanceOf(obj, klass) ? JNI_TRUE : JNI_FALSE;
-    }
-  }
-
   static jmethodID GetMethodID(JNIEnv* env, jclass c, const char* name, const char* sig) {
     ScopedJniThreadState ts(env);
     return FindMethodID(ts, c, name, sig, false);
@@ -2066,11 +2077,6 @@
     UNIMPLEMENTED(FATAL);
   }
 
-  static jboolean ExceptionCheck(JNIEnv* env) {
-    ScopedJniThreadState ts(env);
-    return ts.Self()->IsExceptionPending() ? JNI_TRUE : JNI_FALSE;
-  }
-
   static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) {
     ScopedJniThreadState ts(env);
     UNIMPLEMENTED(FATAL);
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index 62f6068..f205169 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -318,6 +318,20 @@
   // Already tested in NewObjectArray/NewPrimitiveArray.
 }
 
+TEST_F(JniInternalTest, GetObjectClass) {
+  jclass string_class = env_->FindClass("java/lang/String");
+  ASSERT_TRUE(string_class != NULL);
+  jclass class_class = env_->FindClass("java/lang/Class");
+  ASSERT_TRUE(class_class != NULL);
+
+  jstring s = env_->NewStringUTF("poop");
+  jclass c = env_->GetObjectClass(s);
+  ASSERT_TRUE(env_->IsSameObject(string_class, c));
+
+  jclass c2 = env_->GetObjectClass(c);
+  ASSERT_TRUE(env_->IsSameObject(class_class, env_->GetObjectClass(c2)));
+}
+
 TEST_F(JniInternalTest, GetSuperclass) {
   jclass object_class = env_->FindClass("java/lang/Object");
   ASSERT_TRUE(object_class != NULL);
@@ -327,6 +341,16 @@
   ASSERT_TRUE(env_->GetSuperclass(object_class) == NULL);
 }
 
+TEST_F(JniInternalTest, IsAssignableFrom) {
+  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_->IsAssignableFrom(object_class, string_class));
+  ASSERT_FALSE(env_->IsAssignableFrom(string_class, object_class));
+}
+
 TEST_F(JniInternalTest, NewStringUTF) {
   EXPECT_TRUE(env_->NewStringUTF(NULL) == NULL);
   EXPECT_TRUE(env_->NewStringUTF("") != NULL);
@@ -1254,4 +1278,30 @@
 }
 #endif  // __arm__
 
+TEST_F(JniInternalTest, Throw) {
+  EXPECT_EQ(JNI_ERR, env_->Throw(NULL));
+
+  jclass exception_class = env_->FindClass("java/lang/RuntimeException");
+  ASSERT_TRUE(exception_class != NULL);
+  jthrowable exception = reinterpret_cast<jthrowable>(env_->AllocObject(exception_class));
+  ASSERT_TRUE(exception != NULL);
+
+  EXPECT_EQ(JNI_OK, env_->Throw(exception));
+  EXPECT_TRUE(env_->ExceptionCheck());
+  EXPECT_TRUE(env_->IsSameObject(exception, env_->ExceptionOccurred()));
+  env_->ExceptionClear();
+}
+
+TEST_F(JniInternalTest, ThrowNew) {
+  EXPECT_EQ(JNI_ERR, env_->Throw(NULL));
+
+  jclass exception_class = env_->FindClass("java/lang/RuntimeException");
+  ASSERT_TRUE(exception_class != NULL);
+
+  EXPECT_EQ(JNI_OK, env_->ThrowNew(exception_class, "hello world"));
+  EXPECT_TRUE(env_->ExceptionCheck());
+  EXPECT_TRUE(env_->IsInstanceOf(env_->ExceptionOccurred(), exception_class));
+  env_->ExceptionClear();
+}
+
 }
diff --git a/src/object.cc b/src/object.cc
index 6a349ac..20e462a 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -166,6 +166,7 @@
   if (IsStatic()) {
     object = declaring_class_;
   }
+  // TODO: volatile
   return object->GetField32(GetOffset());
 }
 
@@ -174,6 +175,7 @@
   if (IsStatic()) {
     object = declaring_class_;
   }
+  // TODO: volatile
   object->SetField32(GetOffset(), new_value);
 }
 
@@ -182,6 +184,7 @@
   if (IsStatic()) {
     object = declaring_class_;
   }
+  // TODO: volatile
   return object->GetField64(GetOffset());
 }
 
@@ -190,6 +193,7 @@
   if (IsStatic()) {
     object = declaring_class_;
   }
+  // TODO: volatile
   object->SetField64(GetOffset(), new_value);
 }
 
@@ -198,6 +202,7 @@
   if (IsStatic()) {
     object = declaring_class_;
   }
+  // TODO: volatile
   return object->GetFieldObject(GetOffset());
 }
 
@@ -206,6 +211,7 @@
   if (IsStatic()) {
     object = declaring_class_;
   }
+  // TODO: volatile
   object->SetFieldObject(GetOffset(), new_value);
 }
 
diff --git a/src/thread.cc b/src/thread.cc
index d4b7008..e92f23c 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -156,13 +156,7 @@
   return false;
 }
 
-void ThrowNewException(Thread* thread, const char* exception_class_name, const char* msg) {
-  CHECK(thread != NULL);
-
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  Class* exception_class = class_linker->FindSystemClass(exception_class_name);
-  CHECK(exception_class != NULL);
-
+void Thread::ThrowNewException(Class* exception_class, const char* msg) {
   Object* exception = exception_class->NewInstance();
   CHECK(exception != NULL);
 
@@ -179,20 +173,21 @@
                          << exception_class->GetDescriptor()->ToModifiedUtf8() << ".<init> "
                          << "\"" << msg << "\"";
 
-  thread->SetException(exception);
-}
-
-void ThrowNewExceptionV(Thread* thread, const char* exception_class_name, const char* fmt, va_list args) {
-  char msg[512];
-  vsnprintf(msg, sizeof(msg), fmt, args);
-  ThrowNewException(thread, exception_class_name, msg);
+  SetException(exception);
 }
 
 void Thread::ThrowNewException(const char* exception_class_name, const char* fmt, ...) {
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Class* exception_class = class_linker->FindSystemClass(exception_class_name);
+  CHECK(exception_class != NULL);
+
+  std::string msg;
   va_list args;
   va_start(args, fmt);
-  ThrowNewExceptionV(this, exception_class_name, fmt, args);
+  StringAppendV(&msg, fmt, args);
   va_end(args);
+
+  ThrowNewException(exception_class, msg.c_str());
 }
 
 static const char* kStateNames[] = {
diff --git a/src/thread.h b/src/thread.h
index 993327d..6677f7c 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -17,6 +17,7 @@
 
 namespace art {
 
+class Class;
 class ClassLoader;
 class Method;
 class Object;
@@ -195,16 +196,20 @@
     return exception_ != NULL;
   }
 
+  // TODO: Throwable*
   Object* GetException() const {
     return exception_;
   }
 
+  // TODO: Throwable*
   void SetException(Object* new_exception) {
     CHECK(new_exception != NULL);
     // TODO: CHECK(exception_ == NULL);
     exception_ = new_exception;  // TODO
   }
 
+  void ThrowNewException(Class* c, const char* msg);
+
   void ThrowNewException(const char* exception_class_name, const char* fmt, ...)
       __attribute__ ((format(printf, 3, 4)));
 
@@ -381,6 +386,7 @@
   Runtime* runtime_;
 
   // The pending exception or NULL.
+  // TODO: Throwable*
   Object* exception_;
 
   // A non-zero value is used to tell the current thread to enter a safe point