Initial implementation of throw+unwind+Frame.
Unit tests on Exception Handling pass.
Change-Id: I33505dacc6648e72e2ed80c2cca83643cfaef86b
diff --git a/src/class_linker.h b/src/class_linker.h
index 5b629d5..28dfce7 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -271,7 +271,7 @@
FRIEND_TEST(DexCacheTest, Open);
friend class ObjectTest;
FRIEND_TEST(ObjectTest, AllocObjectArray);
- FRIEND_TEST(ExceptionTest, MyClass_F_G);
+ FRIEND_TEST(ExceptionTest, FindExceptionHandler);
DISALLOW_COPY_AND_ASSIGN(ClassLinker);
};
diff --git a/src/dex_file.h b/src/dex_file.h
index ee5cc45..51f5d08 100644
--- a/src/dex_file.h
+++ b/src/dex_file.h
@@ -244,7 +244,7 @@
remaining_count_ = -1;
}
- bool End() const {
+ bool HasNext() const {
return remaining_count_ == -1 && catch_all_ == false;
}
diff --git a/src/exception.h b/src/exception.h
new file mode 100644
index 0000000..5b31e86
--- /dev/null
+++ b/src/exception.h
@@ -0,0 +1,22 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#ifndef ART_SRC_EXCEPTION_H_
+#define ART_SRC_EXCEPTION_H_
+
+#include "class_linker.h"
+#include "dex_file.h"
+#include "object.h"
+#include "thread.h"
+
+namespace art {
+
+struct InternalStackTrace {
+ const Method* method;
+ const uint16_t* pc;
+};
+
+extern InternalStackTrace *GetStackTrace(uint16_t stack_depth);
+
+}
+
+#endif // ART_SRC_EXCEPTION_H_
diff --git a/src/exception_test.cc b/src/exception_test.cc
index a9673ef..99fd954 100644
--- a/src/exception_test.cc
+++ b/src/exception_test.cc
@@ -61,19 +61,46 @@
"IAAAAgAAAAkDAAAAEAAAAQAAACgDAAA=";
class ExceptionTest : public CommonTest {
+ protected:
+ virtual void SetUp() {
+ CommonTest::SetUp();
+
+ dex_.reset(OpenDexFileBase64(kMyClassExceptionHandleDex, "kMyClassExceptionHandleDex"));
+ ASSERT_TRUE(dex_ != NULL);
+ PathClassLoader* class_loader = AllocPathClassLoader(dex_.get());
+ ASSERT_TRUE(class_loader != NULL);
+ my_klass_ = class_linker_->FindClass("Ljava/lang/MyClass;", class_loader);
+ ASSERT_TRUE(my_klass_ != NULL);
+ method_f_ = my_klass_->FindVirtualMethod("f", "()I");
+ ASSERT_TRUE(method_f_ != NULL);
+ method_g_ = my_klass_->FindVirtualMethod("g", "(I)V");
+ ASSERT_TRUE(method_g_ != NULL);
+ }
+
+ DexFile::CatchHandlerItem FindCatchHandlerItem(Method* method,
+ const char exception_type[],
+ uint32_t addr) {
+ const DexFile::CodeItem* code_item = dex_->GetCodeItem(method->code_off_);
+ for (DexFile::CatchHandlerIterator iter = dex_->dexFindCatchHandler(*code_item, addr);
+ !iter.HasNext(); iter.Next()) {
+ if (strcmp(exception_type, dex_->dexStringByTypeIdx(iter.Get().type_idx_)) == 0) {
+ return iter.Get();
+ }
+ }
+ return DexFile::CatchHandlerItem();
+ }
+
+ scoped_ptr<DexFile> dex_;
+
+ Method* method_f_;
+ Method* method_g_;
+
+ private:
+ Class* my_klass_;
};
-TEST_F(ExceptionTest, MyClass_F_G) {
- scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassExceptionHandleDex, "kMyClassExceptionHandleDex"));
- PathClassLoader* class_loader = AllocPathClassLoader(dex.get());
- Class* klass = class_linker_->FindClass("Ljava/lang/MyClass;", class_loader);
- ASSERT_TRUE(klass != NULL);
-
- Method* method_f = klass->FindVirtualMethod("f", "()I");
- ASSERT_TRUE(method_f != NULL);
-
- const DexFile& dex_file = class_linker_->FindDexFile(klass->GetDexCache());
- const DexFile::CodeItem *code_item = dex_file.GetCodeItem(method_f->code_off_);
+TEST_F(ExceptionTest, FindCatchHandler) {
+ const DexFile::CodeItem *code_item = dex_->GetCodeItem(method_f_->code_off_);
ASSERT_TRUE(code_item != NULL);
@@ -81,25 +108,28 @@
ASSERT_NE(0u, code_item->insns_size_);
const struct DexFile::TryItem *t0, *t1;
- t0 = dex_file.dexGetTryItems(*code_item, 0);
- t1 = dex_file.dexGetTryItems(*code_item, 1);
+ t0 = dex_->dexGetTryItems(*code_item, 0);
+ t1 = dex_->dexGetTryItems(*code_item, 1);
EXPECT_LE(t0->start_addr_, t1->start_addr_);
DexFile::CatchHandlerIterator iter =
- dex_file.dexFindCatchHandler(*code_item, 4 /* Dex PC in the first try block */);
- ASSERT_EQ(false, iter.End());
- EXPECT_STREQ("Ljava/io/IOException;", dex_file.dexStringByTypeIdx(iter.Get().type_idx_));
+ dex_->dexFindCatchHandler(*code_item, 4 /* Dex PC in the first try block */);
+ ASSERT_EQ(false, iter.HasNext());
+ EXPECT_STREQ("Ljava/io/IOException;", dex_->dexStringByTypeIdx(iter.Get().type_idx_));
iter.Next();
- ASSERT_EQ(false, iter.End());
- EXPECT_STREQ("Ljava/lang/Exception;", dex_file.dexStringByTypeIdx(iter.Get().type_idx_));
+ ASSERT_EQ(false, iter.HasNext());
+ EXPECT_STREQ("Ljava/lang/Exception;", dex_->dexStringByTypeIdx(iter.Get().type_idx_));
iter.Next();
- ASSERT_EQ(true, iter.End());
+ ASSERT_EQ(true, iter.HasNext());
- iter = dex_file.dexFindCatchHandler(*code_item, 8 /* Dex PC in the second try block */);
- ASSERT_EQ(false, iter.End());
- EXPECT_STREQ("Ljava/io/IOException;", dex_file.dexStringByTypeIdx(iter.Get().type_idx_));
+ iter = dex_->dexFindCatchHandler(*code_item, 8 /* Dex PC in the second try block */);
+ ASSERT_EQ(false, iter.HasNext());
+ EXPECT_STREQ("Ljava/io/IOException;", dex_->dexStringByTypeIdx(iter.Get().type_idx_));
iter.Next();
- ASSERT_EQ(true, iter.End());
+ ASSERT_EQ(true, iter.HasNext());
+
+ iter = dex_->dexFindCatchHandler(*code_item, 11 /* Dex PC not in any try block */);
+ ASSERT_EQ(true, iter.HasNext());
}
} // namespace art
diff --git a/src/object.h b/src/object.h
index 840c966..5a8f5be 100644
--- a/src/object.h
+++ b/src/object.h
@@ -485,6 +485,18 @@
// arguments into an array of arguments.
size_t NumArgArrayBytes() const;
+ // Converts a native PC to a virtual PC. TODO: this is a no-op
+ // until we associate a PC mapping table with each method.
+ uintptr_t ToDexPC(const uintptr_t pc) const {
+ return pc;
+ }
+
+ // Converts a virtual PC to a native PC. TODO: this is a no-op
+ // until we associate a PC mapping table with each method.
+ uintptr_t ToNativePC(const uintptr_t pc) const {
+ return pc;
+ }
+
public: // TODO: private
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
// the class we are a part of
@@ -573,7 +585,9 @@
return code_ != NULL;
}
- void SetCode(const byte* compiled_code, size_t byte_count, InstructionSet set) {
+ void SetCode(const byte* compiled_code,
+ size_t byte_count,
+ InstructionSet set) {
// Copy the code into an executable region.
code_instruction_set_ = set;
code_area_.reset(MemMap::Map(byte_count,
@@ -595,6 +609,18 @@
frame_size_ = frame_size;
}
+ void SetPcOffset(uint32_t pc_offset) {
+ pc_offset_ = pc_offset;
+ }
+
+ size_t GetFrameSize() const {
+ return frame_size_;
+ }
+
+ size_t GetPcOffset() const {
+ return pc_offset_;
+ }
+
void SetCoreSpillMask(uint32_t core_spill_mask) {
core_spill_mask_ = core_spill_mask;
}
@@ -692,6 +718,9 @@
// Size in bytes of compiled code associated with this method
const uint32_t code_size_;
+ // Offset of PC within compiled code (in bytes)
+ uint32_t pc_offset_;
+
// Any native method registered with this method
const void* native_method_;
diff --git a/src/thread.cc b/src/thread.cc
index e92f23c..db4c59f 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -49,6 +49,24 @@
SetOwner(NULL);
}
+void Frame::Next() {
+ byte* next_sp = reinterpret_cast<byte*>(sp_) +
+ GetMethod()->GetFrameSize();
+ sp_ = reinterpret_cast<const Method**>(next_sp);
+}
+
+void* Frame::GetPC() const {
+ byte* pc_addr = reinterpret_cast<byte*>(sp_) +
+ GetMethod()->GetPcOffset();
+ return reinterpret_cast<void*>(pc_addr);
+}
+
+const Method* Frame::NextMethod() const {
+ byte* next_sp = reinterpret_cast<byte*>(sp_) +
+ GetMethod()->GetFrameSize();
+ return reinterpret_cast<const Method*>(next_sp);
+}
+
void* ThreadStart(void *arg) {
UNIMPLEMENTED(FATAL);
return NULL;
@@ -190,6 +208,67 @@
ThrowNewException(exception_class, msg.c_str());
}
+Frame Thread::FindExceptionHandler(void* throw_pc, void** handler_pc) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ DCHECK(class_linker != NULL);
+
+ Frame cur_frame = GetTopOfStack();
+ for (int unwind_depth = 0; ; unwind_depth++) {
+ const Method* cur_method = cur_frame.GetMethod();
+ DexCache* dex_cache = cur_method->GetDeclaringClass()->GetDexCache();
+ const DexFile& dex_file = class_linker->FindDexFile(dex_cache);
+
+ void* handler_addr = FindExceptionHandlerInMethod(cur_method,
+ throw_pc,
+ dex_file,
+ class_linker);
+ if (handler_addr) {
+ *handler_pc = handler_addr;
+ return cur_frame;
+ } else {
+ // Check if we are at the last frame
+ if (cur_frame.HasNext()) {
+ cur_frame.Next();
+ } else {
+ // Either at the top of stack or next frame is native.
+ break;
+ }
+ }
+ }
+ *handler_pc = NULL;
+ return Frame();
+}
+
+void* Thread::FindExceptionHandlerInMethod(const Method* method,
+ void* throw_pc,
+ const DexFile& dex_file,
+ ClassLinker* class_linker) {
+ Object* exception_obj = exception_;
+ exception_ = NULL;
+
+ intptr_t dex_pc = -1;
+ const DexFile::CodeItem* code_item = dex_file.GetCodeItem(method->code_off_);
+ DexFile::CatchHandlerIterator iter;
+ for (iter = dex_file.dexFindCatchHandler(*code_item,
+ method->ToDexPC(reinterpret_cast<intptr_t>(throw_pc)));
+ !iter.HasNext();
+ iter.Next()) {
+ Class* klass = class_linker->FindSystemClass(dex_file.dexStringByTypeIdx(iter.Get().type_idx_));
+ DCHECK(klass != NULL);
+ if (exception_obj->InstanceOf(klass)) {
+ dex_pc = iter.Get().address_;
+ break;
+ }
+ }
+
+ exception_ = exception_obj;
+ if (iter.HasNext()) {
+ return NULL;
+ } else {
+ return reinterpret_cast<void*>( method->ToNativePC(dex_pc) );
+ }
+}
+
static const char* kStateNames[] = {
"New",
"Runnable",
diff --git a/src/thread.h b/src/thread.h
index 6677f7c..b6fad0f 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -113,6 +113,40 @@
void* last_top_of_managed_stack;
};
+// Iterator over managed frames up to the first native-to-managed transition
+class Frame {
+ Frame() : sp_(NULL) {}
+
+ const Method* GetMethod() const {
+ return *sp_;
+ }
+
+ bool HasNext() const {
+ return NextMethod() != NULL;
+ }
+
+ void Next();
+
+ void* GetPC() const;
+
+ const Method** GetSP() const {
+ return sp_;
+ }
+
+ // TODO: this is here for testing, remove when we have exception unit tests
+ // that use the real stack
+ void SetSP(const Method** sp) {
+ sp_ = sp;
+ }
+
+ private:
+ const Method* NextMethod() const;
+
+ friend class Thread;
+
+ const Method** sp_;
+};
+
class Thread {
public:
enum State {
@@ -202,6 +236,16 @@
}
// TODO: Throwable*
+ Frame GetTopOfStack() const {
+ return top_of_managed_stack_;
+ }
+
+ // TODO: this is here for testing, remove when we have exception unit tests
+ // that use the real stack
+ void SetTopOfStack(void* stack) {
+ top_of_managed_stack_.SetSP(reinterpret_cast<const Method**>(stack));
+ }
+
void SetException(Object* new_exception) {
CHECK(new_exception != NULL);
// TODO: CHECK(exception_ == NULL);
@@ -217,6 +261,13 @@
exception_ = NULL;
}
+ Frame FindExceptionHandler(void* throw_pc, void** handler_pc);
+
+ void* FindExceptionHandlerInMethod(const Method* method,
+ void* throw_pc,
+ const DexFile& dex_file,
+ ClassLinker* class_linker);
+
// Offset of exception within Thread, used by generated code
static ThreadOffset ExceptionOffset() {
return ThreadOffset(OFFSETOF_MEMBER(Thread, exception_));
@@ -242,10 +293,6 @@
static bool Init();
- Runtime* GetRuntime() const {
- return runtime_;
- }
-
State GetState() const {
return state_;
}
@@ -275,7 +322,8 @@
// Offset of top of managed stack address, used by generated code
static ThreadOffset TopOfManagedStackOffset() {
- return ThreadOffset(OFFSETOF_MEMBER(Thread, top_of_managed_stack_));
+ return ThreadOffset(OFFSETOF_MEMBER(Thread, top_of_managed_stack_) +
+ OFFSETOF_MEMBER(Frame, sp_));
}
// Offset of top stack handle block within Thread, used by generated code
@@ -313,14 +361,14 @@
// Linked list recording transitions from native to managed code
void PushNativeToManagedRecord(NativeToManagedRecord* record) {
- record->last_top_of_managed_stack = top_of_managed_stack_;
+ record->last_top_of_managed_stack = reinterpret_cast<void*>(top_of_managed_stack_.GetSP());
record->link = native_to_managed_record_;
native_to_managed_record_ = record;
- top_of_managed_stack_ = NULL;
+ top_of_managed_stack_.SetSP(NULL);
}
void PopNativeToManagedRecord(const NativeToManagedRecord& record) {
native_to_managed_record_ = record.link;
- top_of_managed_stack_ = record.last_top_of_managed_stack;
+ top_of_managed_stack_.SetSP( reinterpret_cast<const Method**>(record.last_top_of_managed_stack) );
}
ClassLoader* GetClassLoaderOverride() {
@@ -334,7 +382,7 @@
private:
Thread()
: id_(1234),
- top_of_managed_stack_(NULL),
+ top_of_managed_stack_(),
native_to_managed_record_(NULL),
top_shb_(NULL),
jni_env_(NULL),
@@ -358,7 +406,7 @@
// Top of the managed stack, written out prior to the state transition from
// kRunnable to kNative. Uses include to give the starting point for scanning
// a managed stack when a thread is in native code.
- void* top_of_managed_stack_;
+ Frame top_of_managed_stack_;
// A linked list (of stack allocated records) recording transitions from
// native to managed code.