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.