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) {