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