Add JNI GetArrayLength and start throwing from FindClass.

Change-Id: I080a6ffc5496b47454273acd58c230bda5e04cdd
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 92cf4de..0999788 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -432,6 +432,7 @@
     if (klass != NULL) {
       return klass;
     }
+    Thread::Current()->ClearException();
   }
 
   Thread* self = Thread::Current();
@@ -447,7 +448,9 @@
     DexFile::ClassPath& class_path = ((class_loader != NULL) ? class_loader->GetClassPath() : boot_class_path_);
     DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, class_path);
     if (pair.second == NULL) {
-      LG << "Class " << PrintableString(descriptor) << " not found in class loader " << class_loader;  // TODO: NoClassDefFoundError
+      std::string name(PrintableString(descriptor));
+      self->ThrowNewException("Ljava/lang/NoClassDefFoundError;",
+          "Class %s not found in class loader %p", name.c_str(), class_loader);
       return NULL;
     }
     const DexFile& dex_file = *pair.first;
@@ -515,7 +518,7 @@
     ObjectLock lock(klass);
     // Check for circular dependencies between classes.
     if (!klass->IsLinked() && klass->clinit_thread_id_ == self->GetId()) {
-      LG << "Recursive link";  // TODO: ClassCircularityError
+      self->ThrowNewException("Ljava/lang/ClassCircularityError;", NULL); // TODO: detail
       return NULL;
     }
     // Wait for the pending initialization to complete.
@@ -971,13 +974,11 @@
       return GetClassRoot(kPrimitiveBoolean);
     case 'V':
       return GetClassRoot(kPrimitiveVoid);
-    case 'L':
-    case '[':
-      LOG(ERROR) << "Not a primitive type " << PrintableChar(type);
-    default:
-      LOG(ERROR) << "Unknown primitive type " << PrintableChar(type);
   }
-  return NULL;  // Not reachable.
+  std::string printable_type(PrintableChar(type));
+  Thread::Current()->ThrowNewException("Ljava/lang/NoClassDefFoundError;",
+      "Not a primitive type: %s", printable_type.c_str());
+  return NULL;
 }
 
 bool ClassLinker::InsertClass(const StringPiece& descriptor, Class* klass) {
diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc
index 2583fcf..4fb433f 100644
--- a/src/class_linker_test.cc
+++ b/src/class_linker_test.cc
@@ -12,8 +12,10 @@
 
 class ClassLinkerTest : public CommonTest {
  protected:
-  void AssertNonExistantClass(const StringPiece& descriptor) {
+  void AssertNonExistentClass(const StringPiece& descriptor) {
     EXPECT_TRUE(class_linker_->FindSystemClass(descriptor) == NULL);
+    EXPECT_TRUE(Thread::Current()->IsExceptionPending());
+    Thread::Current()->ClearException();
   }
 
   void AssertPrimitiveClass(const StringPiece& descriptor) {
@@ -218,10 +220,8 @@
 };
 
 TEST_F(ClassLinkerTest, FindClassNonexistent) {
-  Class* result1 = class_linker_->FindSystemClass("NoSuchClass;");
-  EXPECT_TRUE(result1 == NULL);
-  Class* result2 = class_linker_->FindSystemClass("LNoSuchClass;");
-  EXPECT_TRUE(result2 == NULL);
+  AssertNonExistentClass("NoSuchClass;");
+  AssertNonExistentClass("LNoSuchClass;");
 }
 
 TEST_F(ClassLinkerTest, FindClassNested) {
@@ -247,7 +247,7 @@
     char* s = reinterpret_cast<char*>(&ch);
     StringPiece descriptor(s, 1);
     if (expected.find(ch) == StringPiece::npos) {
-      AssertNonExistantClass(descriptor);
+      AssertNonExistentClass(descriptor);
     } else {
       AssertPrimitiveClass(descriptor);
     }
@@ -282,7 +282,7 @@
 
   scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassDex, "kMyClassDex"));
   PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
-  EXPECT_TRUE(linker->FindSystemClass("LMyClass;") == NULL);
+  AssertNonExistentClass("LMyClass;");
   Class* MyClass = linker->FindClass("LMyClass;", class_loader);
   ASSERT_TRUE(MyClass != NULL);
   ASSERT_TRUE(MyClass->GetClass() != NULL);
@@ -319,7 +319,7 @@
   AssertArrayClass("[[C", 2, "C", NULL);
   AssertArrayClass("[[[LMyClass;", 3, "LMyClass;", class_loader);
   // or not available at all
-  AssertNonExistantClass("[[[[LNonExistantClass;");
+  AssertNonExistentClass("[[[[LNonExistentClass;");
 }
 
 TEST_F(ClassLinkerTest, LibCore) {
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 9539b64..a892a28 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -1715,10 +1715,12 @@
     UNIMPLEMENTED(FATAL);
   }
 
-  static jsize GetArrayLength(JNIEnv* env, jarray array) {
+  static jsize GetArrayLength(JNIEnv* env, jarray java_array) {
     ScopedJniThreadState ts(env);
-    UNIMPLEMENTED(FATAL);
-    return 0;
+    Object* obj = Decode<Object*>(ts, java_array);
+    CHECK(obj->IsArray()); // TODO: ReportJniError
+    Array* array = obj->AsArray();
+    return array->GetLength();
   }
 
   static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index) {
diff --git a/src/jni_internal_test.cc b/src/jni_internal_test.cc
index cb71fa2..9043bcd 100644
--- a/src/jni_internal_test.cc
+++ b/src/jni_internal_test.cc
@@ -24,10 +24,13 @@
 }
 
 #define EXPECT_CLASS_FOUND(NAME) \
-  EXPECT_TRUE(env_->FindClass(NAME) != NULL)
+  EXPECT_TRUE(env_->FindClass(NAME) != NULL); \
+  EXPECT_FALSE(env_->ExceptionCheck())
 
 #define EXPECT_CLASS_NOT_FOUND(NAME) \
-  EXPECT_TRUE(env_->FindClass(NAME) == NULL)
+  EXPECT_TRUE(env_->FindClass(NAME) == NULL); \
+  EXPECT_TRUE(env_->ExceptionCheck()); \
+  env_->ExceptionClear()
 
 TEST_F(JniInternalTest, FindClass) {
   // TODO: when these tests start failing because you're calling FindClass
@@ -242,47 +245,62 @@
   EXPECT_FALSE(env_->ExceptionCheck());
 }
 
+#define EXPECT_PRIMITIVE_ARRAY(fn, size, expected_class_name) \
+  do { \
+    jarray a = env_->fn(size); \
+    EXPECT_TRUE(a != NULL); \
+    EXPECT_TRUE(env_->IsInstanceOf(a, \
+        env_->FindClass(expected_class_name))); \
+    EXPECT_EQ(size, env_->GetArrayLength(a)); \
+  } while (false)
+
 TEST_F(JniInternalTest, NewPrimitiveArray) {
   // TODO: death tests for negative array sizes.
 
-  // TODO: check returned array size.
+  EXPECT_PRIMITIVE_ARRAY(NewBooleanArray, 0, "[Z");
+  EXPECT_PRIMITIVE_ARRAY(NewByteArray, 0, "[B");
+  EXPECT_PRIMITIVE_ARRAY(NewCharArray, 0, "[C");
+  EXPECT_PRIMITIVE_ARRAY(NewDoubleArray, 0, "[D");
+  EXPECT_PRIMITIVE_ARRAY(NewFloatArray, 0, "[F");
+  EXPECT_PRIMITIVE_ARRAY(NewIntArray, 0, "[I");
+  EXPECT_PRIMITIVE_ARRAY(NewLongArray, 0, "[J");
+  EXPECT_PRIMITIVE_ARRAY(NewShortArray, 0, "[S");
 
-  // TODO: check returned array class.
-
-  EXPECT_TRUE(env_->NewBooleanArray(0) != NULL);
-  EXPECT_TRUE(env_->NewByteArray(0) != NULL);
-  EXPECT_TRUE(env_->NewCharArray(0) != NULL);
-  EXPECT_TRUE(env_->NewDoubleArray(0) != NULL);
-  EXPECT_TRUE(env_->NewFloatArray(0) != NULL);
-  EXPECT_TRUE(env_->NewIntArray(0) != NULL);
-  EXPECT_TRUE(env_->NewLongArray(0) != NULL);
-  EXPECT_TRUE(env_->NewShortArray(0) != NULL);
-
-  EXPECT_TRUE(env_->NewBooleanArray(1) != NULL);
-  EXPECT_TRUE(env_->NewByteArray(1) != NULL);
-  EXPECT_TRUE(env_->NewCharArray(1) != NULL);
-  EXPECT_TRUE(env_->NewDoubleArray(1) != NULL);
-  EXPECT_TRUE(env_->NewFloatArray(1) != NULL);
-  EXPECT_TRUE(env_->NewIntArray(1) != NULL);
-  EXPECT_TRUE(env_->NewLongArray(1) != NULL);
-  EXPECT_TRUE(env_->NewShortArray(1) != NULL);
+  EXPECT_PRIMITIVE_ARRAY(NewBooleanArray, 1, "[Z");
+  EXPECT_PRIMITIVE_ARRAY(NewByteArray, 1, "[B");
+  EXPECT_PRIMITIVE_ARRAY(NewCharArray, 1, "[C");
+  EXPECT_PRIMITIVE_ARRAY(NewDoubleArray, 1, "[D");
+  EXPECT_PRIMITIVE_ARRAY(NewFloatArray, 1, "[F");
+  EXPECT_PRIMITIVE_ARRAY(NewIntArray, 1, "[I");
+  EXPECT_PRIMITIVE_ARRAY(NewLongArray, 1, "[J");
+  EXPECT_PRIMITIVE_ARRAY(NewShortArray, 1, "[S");
 }
 
 TEST_F(JniInternalTest, NewObjectArray) {
   // TODO: death tests for negative array sizes.
 
-  // TODO: check returned array size.
-
-  // TODO: check returned array class.
-
   // TODO: check non-NULL initial elements.
 
-  jclass c = env_->FindClass("[Ljava/lang/String;");
-  ASSERT_TRUE(c != NULL);
+  jclass element_class = env_->FindClass("java/lang/String");
+  ASSERT_TRUE(element_class != NULL);
+  jclass array_class = env_->FindClass("[Ljava/lang/String;");
+  ASSERT_TRUE(array_class != NULL);
 
-  EXPECT_TRUE(env_->NewObjectArray(0, c, NULL) != NULL);
+  jobjectArray a;
 
-  EXPECT_TRUE(env_->NewObjectArray(1, c, NULL) != NULL);
+  a = env_->NewObjectArray(0, element_class, NULL);
+  EXPECT_TRUE(a != NULL);
+  EXPECT_TRUE(env_->IsInstanceOf(a, array_class));
+  EXPECT_EQ(0, env_->GetArrayLength(a));
+
+  a = env_->NewObjectArray(1, element_class, NULL);
+  EXPECT_TRUE(a != NULL);
+  EXPECT_TRUE(env_->IsInstanceOf(a, array_class));
+  EXPECT_EQ(1, env_->GetArrayLength(a));
+}
+
+TEST_F(JniInternalTest, GetArrayLength) {
+  // Already tested in NewObjectArray/NewPrimitiveArray.
 }
 
 TEST_F(JniInternalTest, NewStringUTF) {