Add semi-functional exception throwing.

We still need a way to call <init> on the new exception instance, in particular.

Change-Id: I839365d20288208cb8847253380cf654a35aeddd
diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc
index 683310d..3b70aa9 100644
--- a/src/class_linker_test.cc
+++ b/src/class_linker_test.cc
@@ -255,8 +255,8 @@
   EXPECT_FALSE(JavaLangObject->IsFinal());
   EXPECT_FALSE(JavaLangObject->IsPrimitive());
   EXPECT_FALSE(JavaLangObject->IsSynthetic());
-  EXPECT_EQ(1U, JavaLangObject->NumDirectMethods());
-  EXPECT_EQ(0U, JavaLangObject->NumVirtualMethods());
+  EXPECT_EQ(2U, JavaLangObject->NumDirectMethods());
+  EXPECT_EQ(11U, JavaLangObject->NumVirtualMethods());
   EXPECT_EQ(0U, JavaLangObject->NumInstanceFields());
   EXPECT_EQ(0U, JavaLangObject->NumStaticFields());
   EXPECT_EQ(0U, JavaLangObject->NumInterfaces());
@@ -305,7 +305,6 @@
 }
 
 TEST_F(ClassLinkerTest, LibCore) {
-  UseLibCoreDex();
   scoped_ptr<DexFile> libcore_dex_file(GetLibCoreDex());
   EXPECT_TRUE(libcore_dex_file.get() != NULL);
   AssertDexFile(libcore_dex_file.get(), NULL);
@@ -315,7 +314,6 @@
 // reorder the fields in the C++ class. Managed class fields are ordered by
 // ClassLinker::LinkInstanceFields.
 TEST_F(ClassLinkerTest, ValidateFieldOrderOfJavaCppUnionClasses) {
-  UseLibCoreDex();
   Class* string = class_linker_->FindSystemClass( "Ljava/lang/String;");
   ASSERT_EQ(4U, string->NumInstanceFields());
   EXPECT_TRUE(string->GetInstanceField(0)->GetName()->Equals("value"));
diff --git a/src/common_test.h b/src/common_test.h
index 9acb06c..9f8d6df 100644
--- a/src/common_test.h
+++ b/src/common_test.h
@@ -17,118 +17,6 @@
 
 // package java.lang;
 // public class Object {}
-
-// package java.lang;
-// public class Class {}
-//
-// package java.lang.reflect;
-// public class Field {}
-//
-// package java.lang.reflect;
-// public class Method {}
-//
-// package java.lang;
-// public class String {
-//   char[] value;
-//   int hashCode;
-//   int offset;
-//   int count;
-// }
-//
-// package java.lang;
-// public class ClassLoader {
-//   Object packages;
-//   ClassLoader parent;
-// }
-//
-// package dalvik.system;
-// public class BaseDexClassLoader extends ClassLoader {
-//   String originalPath;
-//   Object pathList;
-// }
-//
-// package dalvik.system;
-// public class PathClassLoader extends BaseDexClassLoader {}
-//
-// package java.lang;
-// public interface Cloneable {}
-//
-// package java.io;
-// public interface Serializable {}
-//
-// package java.lang;
-// public class StackTraceElement {
-//   String declaringClass;
-//   String methodName;
-//   String fileName;
-//   int lineNumber;
-// }
-//
-// package java.lang;
-// public class Throwable {
-//   String detailMessage;
-//   Throwable cause;
-//   Object suppressedExceptions;
-//   Object stackState;
-//   StackTraceElement[] stackTrace;
-// }
-//
-// package java.lang;
-// public class Exception extends Throwable {}
-//
-// package java.lang;
-// public class NullPointerException extends Exception {}
-//
-static const char kJavaLangDex[] =
-  "ZGV4CjAzNQCEjWBqSq808bjn0gC+ptCv0wtDNadp4vEQCgAAcAAAAHhWNBIAAAAAAAAAAHwJAAAy"
-  "AAAAcAAAABIAAAA4AQAAAQAAAIABAAARAAAAjAEAAAwAAAAUAgAADgAAAHQCAADcBQAANAQAAFAF"
-  "AABYBQAAcQUAAH0FAACPBQAAnwUAAK8FAAC7BQAAvgUAAOIFAAADBgAAGwYAAC4GAABHBgAAXgYA"
-  "AHUGAACXBgAAqwYAAMoGAADeBgAA9QYAABAHAAAsBwAAOQcAAFQHAABhBwAAdwcAAIoHAACiBwAA"
-  "rwcAAL8HAADCBwAAxgcAAOYHAADtBwAA9AcAAAQIAAATCAAAHQgAACcIAAAzCAAAPwgAAEcIAABV"
-  "CAAAXwgAAGcIAABxCAAAfQgAAIkIAACfCAAABwAAAAgAAAAJAAAACgAAAAsAAAAMAAAADQAAAA4A"
-  "AAAPAAAAEAAAABEAAAASAAAAEwAAABQAAAAVAAAAHgAAAB8AAAAgAAAAHgAAAA8AAAAAAAAAAQAL"
-  "ACoAAAABAAkALQAAAAUACQArAAAABQAFACwAAAAKAAsAIwAAAAoACwAlAAAACgAAACcAAAAKAAsA"
-  "KAAAAAsAAAAiAAAACwAAACYAAAALAAAAKQAAAAsAEAAxAAAADAAMACEAAAAMAAsAJAAAAAwACQAu"
-  "AAAADAARAC8AAAAMAAkAMAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAAAUAAAAAAAAABwAAAAAA"
-  "AAAIAAAAAAAAAAkAAAAAAAAACgAAAAAAAAALAAAAAAAAAAwAAAAAAAAADQAAAAAAAAAOAAAAAAAA"
-  "AAkAAAABAAAA/////wAAAAAYAAAAAAAAAOIIAAAAAAAABQAAAAEAAAAJAAAAAAAAAAMAAAAAAAAA"
-  "7AgAAAAAAAABAAAAAQAAAAUAAAAAAAAAAQAAAAAAAAD6CAAAAAAAAAIAAAABAAAAAQAAAAAAAAAZ"
-  "AAAAAAAAAAgJAAAAAAAAAwAAAAEGAAAJAAAAAAAAABoAAAAAAAAAAAAAAAAAAAAEAAAAAQAAAAkA"
-  "AAAAAAAAAgAAAAAAAAASCQAAAAAAAAYAAAABBgAACQAAAAAAAAAEAAAAAAAAAAAAAAAAAAAADAAA"
-  "AAEAAAAJAAAAAAAAAB0AAAAAAAAAHAkAAAAAAAAHAAAAAQAAAAwAAAAAAAAABQAAAAAAAAAwCQAA"
-  "AAAAAAgAAAABAAAABwAAAAAAAAAXAAAAAAAAADoJAAAAAAAACgAAAAEAAAAJAAAAAAAAABsAAAAA"
-  "AAAARAkAAAAAAAALAAAAAQAAAAkAAAAAAAAAHAAAAAAAAABWCQAAAAAAAA0AAAABAAAACQAAAAAA"
-  "AAAGAAAAAAAAAGgJAAAAAAAADgAAAAEAAAAJAAAAAAAAABYAAAAAAAAAcgkAAAAAAAABAAEAAAAA"
-  "AKYIAAABAAAADgAAAAEAAQABAAAAqwgAAAQAAABwEAYAAAAOAAEAAQABAAAAsAgAAAQAAABwEAMA"
-  "AAAOAAEAAQABAAAAtQgAAAQAAABwEAAAAAAOAAEAAQABAAAAuggAAAQAAABwEAYAAAAOAAEAAQAB"
-  "AAAAvwgAAAQAAABwEAYAAAAOAAEAAQABAAAAxAgAAAQAAABwEAkAAAAOAAEAAQABAAAAyQgAAAQA"
-  "AABwEAQAAAAOAAEAAQABAAAAzggAAAQAAABwEAYAAAAOAAEAAQABAAAA0wgAAAQAAABwEAYAAAAO"
-  "AAEAAQABAAAA2AgAAAQAAABwEAYAAAAOAAEAAQABAAAA3QgAAAQAAABwEAYAAAAOAAY8aW5pdD4A"
-  "F0Jhc2VEZXhDbGFzc0xvYWRlci5qYXZhAApDbGFzcy5qYXZhABBDbGFzc0xvYWRlci5qYXZhAA5D"
-  "bG9uZWFibGUuamF2YQAORXhjZXB0aW9uLmphdmEACkZpZWxkLmphdmEAAUkAIkxkYWx2aWsvc3lz"
-  "dGVtL0Jhc2VEZXhDbGFzc0xvYWRlcjsAH0xkYWx2aWsvc3lzdGVtL1BhdGhDbGFzc0xvYWRlcjsA"
-  "FkxqYXZhL2lvL1NlcmlhbGl6YWJsZTsAEUxqYXZhL2xhbmcvQ2xhc3M7ABdMamF2YS9sYW5nL0Ns"
-  "YXNzTG9hZGVyOwAVTGphdmEvbGFuZy9DbG9uZWFibGU7ABVMamF2YS9sYW5nL0V4Y2VwdGlvbjsA"
-  "IExqYXZhL2xhbmcvTnVsbFBvaW50ZXJFeGNlcHRpb247ABJMamF2YS9sYW5nL09iamVjdDsAHUxq"
-  "YXZhL2xhbmcvU3RhY2tUcmFjZUVsZW1lbnQ7ABJMamF2YS9sYW5nL1N0cmluZzsAFUxqYXZhL2xh"
-  "bmcvVGhyb3dhYmxlOwAZTGphdmEvbGFuZy9yZWZsZWN0L0ZpZWxkOwAaTGphdmEvbGFuZy9yZWZs"
-  "ZWN0L01ldGhvZDsAC01ldGhvZC5qYXZhABlOdWxsUG9pbnRlckV4Y2VwdGlvbi5qYXZhAAtPYmpl"
-  "Y3QuamF2YQAUUGF0aENsYXNzTG9hZGVyLmphdmEAEVNlcmlhbGl6YWJsZS5qYXZhABZTdGFja1Ry"
-  "YWNlRWxlbWVudC5qYXZhAAtTdHJpbmcuamF2YQAOVGhyb3dhYmxlLmphdmEAAVYAAltDAB5bTGph"
-  "dmEvbGFuZy9TdGFja1RyYWNlRWxlbWVudDsABWNhdXNlAAVjb3VudAAOZGVjbGFyaW5nQ2xhc3MA"
-  "DWRldGFpbE1lc3NhZ2UACGZpbGVOYW1lAAhoYXNoQ29kZQAKbGluZU51bWJlcgAKbWV0aG9kTmFt"
-  "ZQAGb2Zmc2V0AAxvcmlnaW5hbFBhdGgACHBhY2thZ2VzAAZwYXJlbnQACHBhdGhMaXN0AApzdGFj"
-  "a1N0YXRlAApzdGFja1RyYWNlABRzdXBwcmVzc2VkRXhjZXB0aW9ucwAFdmFsdWUAAwAHDgAFAAcO"
-  "AAUABw4ABQAHDgAFAAcOAAUABw4ABQAHDgAFAAcOAAUABw4ABQAHDgAFAAcOAAUABw4AAAABAAaB"
-  "gAS0CAACAQACAAEAA4GABMgIAAIBAAAAAQAAgYAE4AgAAAEAAYGABPgIAAABAAKBgASQCQAFAQAM"
-  "AAEAAQABAAEACYGABKgJAAABAASBgATACQAAAQAFgYAE2AkABAEABAABAAEAAQAHgYAE8AkABAEA"
-  "CAABAAEAAQAIgYAEiAoAAAEACoGABKAKAAABAAuBgAS4CgwAAAAAAAAAAQAAAAAAAAABAAAAMgAA"
-  "AHAAAAACAAAAEgAAADgBAAADAAAAAQAAAIABAAAEAAAAEQAAAIwBAAAFAAAADAAAABQCAAAGAAAA"
-  "DgAAAHQCAAABIAAADAAAADQEAAACIAAAMgAAAFAFAAADIAAADAAAAKYIAAAAIAAADAAAAOIIAAAA"
-  "EAAAAQAAAHwJAAA=";
-
-// package java.lang;
-// public class Object {}
 //
 // class MyClass {}
 static const char kMyClassDex[] =
@@ -443,7 +331,7 @@
     int mkdir_result = mkdir(art_cache_.c_str(), 0700);
     ASSERT_EQ(mkdir_result, 0);
 
-    java_lang_dex_file_.reset(OpenDexFileBase64(kJavaLangDex));
+    java_lang_dex_file_.reset(GetLibCoreDex());
 
     std::vector<DexFile*> boot_class_path;
     boot_class_path.push_back(java_lang_dex_file_.get());
@@ -496,18 +384,6 @@
     return DexFile::OpenZip(libcore_dex_file_name);
   }
 
-  void UseLibCoreDex() {
-    delete runtime_.release();
-    java_lang_dex_file_.reset(GetLibCoreDex());
-
-    std::vector<DexFile*> boot_class_path;
-    boot_class_path.push_back(java_lang_dex_file_.get());
-
-    runtime_.reset(Runtime::Create(boot_class_path));
-    ASSERT_TRUE(runtime_ != NULL);
-    class_linker_ = runtime_->GetClassLinker();
-  }
-
   PathClassLoader* AllocPathClassLoader(const DexFile* dex_file) {
     class_linker_->RegisterDexFile(dex_file);
     std::vector<const DexFile*> dex_files;
diff --git a/src/image_test.cc b/src/image_test.cc
index 80ccfa0..5de860e 100644
--- a/src/image_test.cc
+++ b/src/image_test.cc
@@ -10,7 +10,6 @@
 class ImageTest : public RuntimeTest {};
 
 TEST_F(ImageTest, WriteRead) {
-  UseLibCoreDex();
   scoped_ptr<DexFile> libcore_dex_file(GetLibCoreDex());
   EXPECT_TRUE(libcore_dex_file.get() != NULL);
 
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 9a2492e..eda3e9b 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -215,10 +215,10 @@
     result += ';';
   }
   // Rewrite '.' as '/' for backwards compatibility.
-  for (size_t i = 0; i < result.size(); ++i) {
-    if (result[i] == '.') {
-      result[i] = '/';
-    }
+  if (result.find('.') != std::string::npos) {
+    LOG(WARNING) << "Call to JNI FindClass with dots in name: "
+                 << "\"" << name << "\"";
+    std::replace(result.begin(), result.end(), '.', '/');
   }
   return result;
 }
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index 9394ff6..569dfda 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -115,7 +115,19 @@
   EXPECT_TRUE(array != NULL);
   env_->SetObjectArrayElement(array, 0, c);
   // TODO: check reading value back
-  // TODO: check IndexOutOfBoundsExceptions thrown for bad indexes.
+
+  // ArrayIndexOutOfBounds for negative index.
+  // TODO: check exception type
+  env_->SetObjectArrayElement(array, -1, c);
+  EXPECT_TRUE(env_->ExceptionCheck());
+  env_->ExceptionClear();
+
+  // ArrayIndexOutOfBounds for too-large index.
+  // TODO: check exception type
+  env_->SetObjectArrayElement(array, 1, c);
+  EXPECT_TRUE(env_->ExceptionCheck());
+  env_->ExceptionClear();
+
   // TODO: check ArrayStoreException thrown for bad types.
 }
 
diff --git a/src/object.h b/src/object.h
index abcf51f..4da6725 100644
--- a/src/object.h
+++ b/src/object.h
@@ -628,11 +628,9 @@
  protected:
   bool IsValidIndex(int32_t index) const {
     if (index < 0 || index >= length_) {
-      // TODO: throw ArrayIndexOutOfBoundsException with the detail message
-      // "length=%d; index=%d", length_, index;
-      CHECK(false) << "ArrayIndexOutOfBoundsException: length="
-                   << length_ << "; index=" << index;
-      return false;
+      Thread* self = Thread::Current();
+      self->ThrowNewException("Ljava/lang/ArrayIndexOutOfBoundsException;",
+          "length=%i; index=%i", length_, index);
     }
     return true;
   }
@@ -1275,10 +1273,13 @@
     return count_;
   }
 
+  // TODO: do we need this? Equals is the only caller, and could
+  // bounds check itself.
   uint16_t CharAt(int32_t index) const {
     if (index < 0 || index >= count_) {
-      // TODO: throw new StringIndexOutOfBounds(this, index);
-      CHECK(false) << "StringIndexOutOfBounds: " << index;
+      Thread* self = Thread::Current();
+      self->ThrowNewException("Ljava/lang/StringIndexOutOfBoundsException;",
+          "length=%i; index=%i", count_, index);
       return 0;
     } else {
       return GetCharArray()->Get(index + GetOffset());
@@ -1414,6 +1415,7 @@
     hash_code_ = ComputeUtf16Hash(array_->GetData(), count_);
   }
 
+  // TODO: do we need this overload? give it a more intention-revealing name.
   bool Equals(const char* modified_utf8) const {
     for (uint32_t i = 0; i < GetLength(); ++i) {
       uint16_t ch = GetUtf16FromUtf8(&modified_utf8);
@@ -1424,6 +1426,7 @@
     return *modified_utf8 == '\0';
   }
 
+  // TODO: do we need this overload? give it a more intention-revealing name.
   bool Equals(const StringPiece& modified_utf8) const {
     // TODO: do not assume C-string representation.
     return Equals(modified_utf8.data());
@@ -1442,6 +1445,7 @@
     return true;
   }
 
+  // TODO: do we need this overload? give it a more intention-revealing name.
   bool Equals(const uint16_t* that_chars, uint32_t that_offset, uint32_t that_length) const {
     if (this->GetLength() != that_length) {
       return false;
diff --git a/src/thread.cc b/src/thread.cc
index 1dee238..51b72ce 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -8,6 +8,8 @@
 #include <cerrno>
 #include <list>
 
+#include "class_linker.h"
+#include "object.h"
 #include "runtime.h"
 #include "utils.h"
 
@@ -128,6 +130,46 @@
   return true;
 }
 
+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);
+
+  Object* exception = exception_class->NewInstance();
+  CHECK(exception != NULL);
+
+  size_t char_count = String::ModifiedUtf8Len(msg);
+  String* java_msg = String::AllocFromModifiedUtf8(char_count, 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");
+
+  // TODO: need to *call* the constructor!
+  UNIMPLEMENTED(WARNING) << "can't call "
+                         << exception_class->GetDescriptor() << "."
+                         << ctor->GetDescriptor() << " "
+                         << "\"" << 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);
+}
+
+void Thread::ThrowNewException(const char* exception_class_name, const char* fmt, ...) {
+  va_list args;
+  va_start(args, fmt);
+  ThrowNewExceptionV(this, exception_class_name, fmt, args);
+  va_end(args);
+}
+
 static const char* kStateNames[] = {
   "New",
   "Runnable",
diff --git a/src/thread.h b/src/thread.h
index e065f68..8bde370 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -147,6 +147,9 @@
     exception_ = new_exception;  // TODO
   }
 
+  void ThrowNewException(const char* exception_class_name, const char* fmt, ...)
+      __attribute__ ((format(printf, 3, 4)));
+
   void ClearException() {
     exception_ = NULL;
   }