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.