Add "class Throwable" and rewrite exception throwing to use JNI.

Change-Id: I79836075337eedfc5923ebff028176615ffd3598
diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc
index 7d5a447..ce08b1b 100644
--- a/src/class_linker_test.cc
+++ b/src/class_linker_test.cc
@@ -243,9 +243,7 @@
   EXPECT_EQ(1U, inner->NumDirectMethods());
 }
 
-TEST_F(ClassLinkerTest, FindClass) {
-  ClassLinker* linker = class_linker_;
-
+TEST_F(ClassLinkerTest, FindClass_Primitives) {
   StringPiece expected = "BCDFIJSZV";
   for (int ch = 0; ch < 255; ch++) {
     char* s = reinterpret_cast<char*>(&ch);
@@ -256,8 +254,10 @@
       AssertPrimitiveClass(descriptor);
     }
   }
+}
 
-  Class* JavaLangObject = linker->FindSystemClass("Ljava/lang/Object;");
+TEST_F(ClassLinkerTest, FindClass) {
+  Class* JavaLangObject = class_linker_->FindSystemClass("Ljava/lang/Object;");
   ASSERT_TRUE(JavaLangObject != NULL);
   ASSERT_TRUE(JavaLangObject->GetClass() != NULL);
   ASSERT_EQ(JavaLangObject->GetClass(), JavaLangObject->GetClass()->GetClass());
@@ -283,11 +283,10 @@
   EXPECT_EQ(0U, JavaLangObject->NumStaticFields());
   EXPECT_EQ(0U, JavaLangObject->NumInterfaces());
 
-
   scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassDex, "kMyClassDex"));
   PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
   AssertNonExistentClass("LMyClass;");
-  Class* MyClass = linker->FindClass("LMyClass;", class_loader);
+  Class* MyClass = class_linker_->FindClass("LMyClass;", class_loader);
   ASSERT_TRUE(MyClass != NULL);
   ASSERT_TRUE(MyClass->GetClass() != NULL);
   ASSERT_EQ(MyClass->GetClass(), MyClass->GetClass()->GetClass());
@@ -343,6 +342,14 @@
   EXPECT_TRUE(string->GetInstanceField(2)->GetName()->Equals("offset"));
   EXPECT_TRUE(string->GetInstanceField(3)->GetName()->Equals("count"));
 
+  Class* throwable = class_linker_->FindSystemClass( "Ljava/lang/Throwable;");
+  ASSERT_EQ(5U, throwable->NumInstanceFields());
+  EXPECT_TRUE(throwable->GetInstanceField(0)->GetName()->Equals("cause"));
+  EXPECT_TRUE(throwable->GetInstanceField(1)->GetName()->Equals("detailMessage"));
+  EXPECT_TRUE(throwable->GetInstanceField(2)->GetName()->Equals("stackState"));
+  EXPECT_TRUE(throwable->GetInstanceField(3)->GetName()->Equals("stackTrace"));
+  EXPECT_TRUE(throwable->GetInstanceField(4)->GetName()->Equals("suppressedExceptions"));
+
   Class* accessible_object = class_linker_->FindSystemClass("Ljava/lang/reflect/AccessibleObject;");
   ASSERT_EQ(1U, accessible_object->NumInstanceFields());
   EXPECT_TRUE(accessible_object->GetInstanceField(0)->GetName()->Equals("flag"));
diff --git a/src/jni_compiler_test.cc b/src/jni_compiler_test.cc
index f315d1f..29e8942 100644
--- a/src/jni_compiler_test.cc
+++ b/src/jni_compiler_test.cc
@@ -367,7 +367,7 @@
   EXPECT_EQ(1, gJava_MyClass_foo_calls);
   EXPECT_EQ(0, gExceptionHandler_calls);
   // TODO: create a real exception here
-  Thread::Current()->SetException(reinterpret_cast<Object*>(jobj_));
+  Thread::Current()->SetException(reinterpret_cast<Throwable*>(jobj_));
   env_->CallNonvirtualVoidMethod(jobj_, jklass_, jmethod_);
   EXPECT_EQ(2, gJava_MyClass_foo_calls);
   EXPECT_EQ(1, gExceptionHandler_calls);
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 9aa23bb..0d46f29 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -218,9 +218,9 @@
   if (ts.Env()->check_jni) {
     size_t entry_count = locals.Capacity();
     if (entry_count > 16) {
-      std::string class_name(PrettyDescriptor(obj->GetClass()->GetDescriptor()));
+      std::string class_descriptor(PrettyDescriptor(obj->GetClass()->GetDescriptor()));
       LOG(WARNING) << "Warning: more than 16 JNI local references: "
-                   << entry_count << " (most recent was a " << class_name << ")";
+                   << entry_count << " (most recent was a " << class_descriptor << ")";
       locals.Dump();
       // TODO: dvmDumpThread(dvmThreadSelf(), false);
       // dvmAbort();
@@ -514,10 +514,10 @@
 
   if (field == NULL) {
     Thread* self = Thread::Current();
-    std::string class_name(c->GetDescriptor()->ToModifiedUtf8());
+    std::string class_descriptor(c->GetDescriptor()->ToModifiedUtf8());
     self->ThrowNewException("Ljava/lang/NoSuchFieldError;",
         "no \"%s\" field \"%s\" in class \"%s\" or its superclasses", sig,
-        name, class_name.c_str());
+        name, class_descriptor.c_str());
     return NULL;
   }
 
@@ -616,7 +616,7 @@
 
   static jint Throw(JNIEnv* env, jthrowable java_exception) {
     ScopedJniThreadState ts(env);
-    Object* exception = Decode<Object*>(ts, java_exception);
+    Throwable* exception = Decode<Throwable*>(ts, java_exception);
     if (exception == NULL) {
       return JNI_ERR;
     }
@@ -624,10 +624,32 @@
     return JNI_OK;
   }
 
-  static jint ThrowNew(JNIEnv* env, jclass java_class, const char* msg) {
+  static jint ThrowNew(JNIEnv* env, jclass c, const char* msg) {
     ScopedJniThreadState ts(env);
-    Class* c = Decode<Class*>(ts, java_class);
-    ts.Self()->ThrowNewException(c, msg);
+    // TODO: check for a pending exception to decide what constructor to call.
+    jmethodID mid = env->GetMethodID(c, "<init>", "(Ljava/lang/String;)V");
+    if (mid == NULL) {
+      return JNI_ERR;
+    }
+    jstring s = env->NewStringUTF(msg);
+    if (s == NULL) {
+      return JNI_ERR;
+    }
+
+    jvalue args[1];
+    args[0].l = s;
+    jthrowable exception = reinterpret_cast<jthrowable>(env->NewObjectA(c, mid, args));
+    if (exception == NULL) {
+      return JNI_ERR;
+    }
+
+    LOG(INFO) << "Throwing " << PrettyType(Decode<Throwable*>(ts, exception))
+              << ": " << msg;
+    ts.Self()->SetException(Decode<Throwable*>(ts, exception));
+
+    env->DeleteLocalRef(exception);
+    env->DeleteLocalRef(s);
+
     return JNI_OK;
   }
 
@@ -1994,17 +2016,17 @@
       }
       if (method == NULL) {
         Thread* self = Thread::Current();
-        std::string class_name = klass->GetDescriptor()->ToModifiedUtf8();
+        std::string class_descriptor(klass->GetDescriptor()->ToModifiedUtf8());
         self->ThrowNewException("Ljava/lang/NoSuchMethodError;",
             "no method \"%s.%s%s\"",
-            class_name.c_str(), name, sig);
+            class_descriptor.c_str(), name, sig);
         return JNI_ERR;
       } else if (!method->IsNative()) {
         Thread* self = Thread::Current();
-        std::string class_name = klass->GetDescriptor()->ToModifiedUtf8();
+        std::string class_descriptor(klass->GetDescriptor()->ToModifiedUtf8());
         self->ThrowNewException("Ljava/lang/NoSuchMethodError;",
             "method \"%s.%s%s\" is not native",
-            class_name.c_str(), name, sig);
+            class_descriptor.c_str(), name, sig);
         return JNI_ERR;
       }
       method->RegisterNative(methods[i].fnPtr);
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index f205169..ee4f2b1 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -260,12 +260,12 @@
   EXPECT_FALSE(env_->ExceptionCheck());
 }
 
-#define EXPECT_PRIMITIVE_ARRAY(fn, size, expected_class_name) \
+#define EXPECT_PRIMITIVE_ARRAY(fn, size, expected_class_descriptor) \
   do { \
     jarray a = env_->fn(size); \
     EXPECT_TRUE(a != NULL); \
     EXPECT_TRUE(env_->IsInstanceOf(a, \
-        env_->FindClass(expected_class_name))); \
+        env_->FindClass(expected_class_descriptor))); \
     EXPECT_EQ(size, env_->GetArrayLength(a)); \
   } while (false)
 
diff --git a/src/main.cc b/src/main.cc
index b4eb151..0330d0d 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -77,7 +77,7 @@
   // Find [class].main(String[]).
 
   // Convert "com.android.Blah" to "com/android/Blah".
-  std::string class_name = argv[0];
+  std::string class_name(argv[0]);
   std::replace(class_name.begin(), class_name.end(), '.', '/');
 
   ScopedLocalRef<jclass> klass(env, env->FindClass(class_name.c_str()));
diff --git a/src/object.h b/src/object.h
index 5a8f5be..0b1c072 100644
--- a/src/object.h
+++ b/src/object.h
@@ -1715,6 +1715,18 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(String);
 };
 
+class Throwable : public Object {
+ private:
+  // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
+  Throwable* cause_;
+  String* detail_message_;
+  Object* stack_state_; // Note this is Java volatile:
+  Object* stack_trace_;
+  Object* suppressed_exceptions_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(Throwable);
+};
+
 inline bool Object::IsString() const {
   // TODO use "klass_ == String::GetJavaLangString()" instead?
   return klass_ == klass_->descriptor_->klass_;
diff --git a/src/thread.cc b/src/thread.cc
index db4c59f..30012b9 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -174,38 +174,24 @@
   return false;
 }
 
-void Thread::ThrowNewException(Class* exception_class, const char* msg) {
-  Object* exception = exception_class->NewInstance();
-  CHECK(exception != NULL);
-
-  String* java_msg = String::AllocFromModifiedUtf8(msg);
-  CHECK(java_msg != NULL);
-
-  // TODO: what if there's already a pending exception?
-  // TODO: support the other constructors.
-  Method* ctor = exception_class->FindDirectMethod("<init>", "(Ljava/lang/String;)V");
-  CHECK(ctor != NULL);
-
-  // TODO: need to *call* the constructor!
-  UNIMPLEMENTED(WARNING) << "can't call "
-                         << exception_class->GetDescriptor()->ToModifiedUtf8() << ".<init> "
-                         << "\"" << 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);
-
+void Thread::ThrowNewException(const char* exception_class_descriptor, const char* fmt, ...) {
   std::string msg;
   va_list args;
   va_start(args, fmt);
   StringAppendV(&msg, fmt, args);
   va_end(args);
 
-  ThrowNewException(exception_class, msg.c_str());
+  // Convert "Ljava/lang/Exception;" into JNI-style "java/lang/Exception".
+  CHECK(exception_class_descriptor[0] == 'L');
+  std::string descriptor(exception_class_descriptor + 1);
+  CHECK(descriptor[descriptor.length() - 1] == ';');
+  descriptor.erase(descriptor.length() - 1);
+
+  JNIEnv* env = GetJniEnv();
+  jclass exception_class = env->FindClass(descriptor.c_str());
+  CHECK(exception_class != NULL) << "descriptor=\"" << descriptor << "\"";
+  int rc = env->ThrowNew(exception_class, msg.c_str());
+  CHECK_EQ(rc, JNI_OK);
 }
 
 Frame Thread::FindExceptionHandler(void* throw_pc, void** handler_pc) {
@@ -243,7 +229,7 @@
                                            void* throw_pc,
                                            const DexFile& dex_file,
                                            ClassLinker* class_linker) {
-  Object* exception_obj = exception_;
+  Throwable* exception_obj = exception_;
   exception_ = NULL;
 
   intptr_t dex_pc = -1;
diff --git a/src/thread.h b/src/thread.h
index b6fad0f..979a3e3 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -25,6 +25,7 @@
 class StackHandleBlock;
 class Thread;
 class ThreadList;
+class Throwable;
 
 class Mutex {
  public:
@@ -230,12 +231,10 @@
     return exception_ != NULL;
   }
 
-  // TODO: Throwable*
-  Object* GetException() const {
+  Throwable* GetException() const {
     return exception_;
   }
 
-  // TODO: Throwable*
   Frame GetTopOfStack() const {
     return top_of_managed_stack_;
   }
@@ -246,15 +245,13 @@
     top_of_managed_stack_.SetSP(reinterpret_cast<const Method**>(stack));
   }
 
-  void SetException(Object* new_exception) {
+  void SetException(Throwable* 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, ...)
+  void ThrowNewException(const char* exception_class_descriptor, const char* fmt, ...)
       __attribute__ ((format(printf, 3, 4)));
 
   void ClearException() {
@@ -434,8 +431,7 @@
   Runtime* runtime_;
 
   // The pending exception or NULL.
-  // TODO: Throwable*
-  Object* exception_;
+  Throwable* exception_;
 
   // A non-zero value is used to tell the current thread to enter a safe point
   // at the next poll.