Exception Handling: libdex integration. Also added unit test on exception.
Runtime processing of TryItem and CatchHandler. Added iterator.
Next step: Exception Handling: RT integration. Implement throw and
unwind.
Change-Id: Idf88ce83e37b004016f1eca2c621e5a86948fe91
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 9053d57..2aed455 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -489,6 +489,7 @@
dst->descriptor_ = String::AllocFromModifiedUtf8(utf16_length, utf8.get());
}
dst->proto_idx_ = method_id.proto_idx_;
+ dst->code_off_ = src.code_off_;
dst->shorty_ = dex_file.GetShorty(method_id.proto_idx_);
dst->access_flags_ = src.access_flags_;
@@ -499,7 +500,6 @@
dst->num_registers_ = code_item->registers_size_;
dst->num_ins_ = code_item->ins_size_;
dst->num_outs_ = code_item->outs_size_;
- dst->insns_ = code_item->insns_;
} else {
uint16_t num_args = dst->NumArgRegisters();
if (!dst->IsStatic()) {
@@ -1340,7 +1340,9 @@
for (size_t i = 0; i < klass->NumVirtualMethods(); ++i) {
Method* method = klass->GetVirtualMethod(i);
if (method->IsAbstract()) {
- method->insns_ = reinterpret_cast<uint16_t*>(0xFFFFFFFF); // TODO: AbstractMethodError
+ LG << "AbstractMethodError";
+ method->code_off_ = 0xFFFFFFFF;
+ // TODO: throw AbstractMethodError
}
}
}
diff --git a/src/class_linker.h b/src/class_linker.h
index edd48eb..fa50537 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -201,6 +201,7 @@
FRIEND_TEST(DexCacheTest, Open);
friend class ObjectTest;
FRIEND_TEST(ObjectTest, AllocObjectArray);
+ FRIEND_TEST(ExceptionTest, MyClass_F_G);
DISALLOW_COPY_AND_ASSIGN(ClassLinker);
};
diff --git a/src/dex_file.h b/src/dex_file.h
index 014bd6e..373ec51 100644
--- a/src/dex_file.h
+++ b/src/dex_file.h
@@ -12,6 +12,7 @@
#include "scoped_ptr.h"
#include "stringpiece.h"
#include "strutil.h"
+#include "utils.h"
namespace art {
@@ -183,6 +184,11 @@
uint16_t insns_[1];
};
+ struct CatchHandlerItem {
+ uint32_t type_idx_; // type index of the caught exception type
+ uint32_t address_; // handler address
+ };
+
// Raw try_item.
struct TryItem {
uint32_t start_addr_;
@@ -190,6 +196,64 @@
uint16_t handler_off_;
};
+ class CatchHandlerIterator {
+ public:
+ CatchHandlerIterator() {
+ remaining_count_ = -1;
+ catch_all_ = false;
+ }
+
+ CatchHandlerIterator(const byte* handler_data) {
+ current_data_ = handler_data;
+ remaining_count_ = DecodeUnsignedLeb128(¤t_data_);
+
+ // If remaining_count_ is non-positive, then it is the negative of
+ // the number of catch types, and the catches are followed by a
+ // catch-all handler.
+ if (remaining_count_ <= 0) {
+ catch_all_ = true;
+ remaining_count_ = -remaining_count_;
+ } else {
+ catch_all_ = false;
+ }
+ Next();
+ }
+
+ CatchHandlerItem& Get() {
+ return handler_;
+ }
+
+ void Next() {
+ if (remaining_count_ > 0) {
+ handler_.type_idx_ = DecodeUnsignedLeb128(¤t_data_);
+ handler_.address_ = DecodeUnsignedLeb128(¤t_data_);
+ remaining_count_--;
+ return;
+ }
+
+ if (catch_all_) {
+ handler_.type_idx_ = kDexNoIndex;
+ handler_.address_ = DecodeUnsignedLeb128(¤t_data_);
+ catch_all_ = false;
+ return;
+ }
+
+ // no more handler
+ remaining_count_ = -1;
+ }
+
+ bool End() const {
+ return remaining_count_ < 0 && catch_all_ == false;
+ }
+
+ private:
+ CatchHandlerItem handler_;
+ const byte *current_data_; // the current handlder in dex file.
+ int32_t remaining_count_; // number of handler not read.
+ bool catch_all_; // is there a handler that will catch all exceptions in case
+ // that all typed handler does not match.
+ };
+
// Partially decoded form of class_data_item.
struct ClassDataHeader {
uint32_t static_fields_size_; // the number of static fields
@@ -349,10 +413,14 @@
}
const CodeItem* GetCodeItem(const Method& method) const {
- if (method.code_off_ == 0) {
+ return GetCodeItem(method.code_off_);
+ }
+
+ const CodeItem* GetCodeItem(const uint32_t code_off_) const {
+ if (code_off_ == 0) {
return NULL; // native or abstract method
} else {
- const byte* addr = base_ + method.code_off_;
+ const byte* addr = base_ + code_off_;
return reinterpret_cast<const CodeItem*>(addr);
}
}
@@ -449,6 +517,86 @@
*last_idx = idx;
}
+ const TryItem* dexGetTryItems(const CodeItem& code_item, uint32_t offset) const {
+ const uint16_t* insns_end_ = &code_item.insns_[code_item.insns_size_];
+ return reinterpret_cast<const TryItem*>
+ (RoundUp(reinterpret_cast<uint32_t>(insns_end_), 4)) + offset;
+ }
+
+ // Get the base of the encoded data for the given DexCode.
+ const byte* dexGetCatchHandlerData(const CodeItem& code_item, uint32_t offset) const {
+ const byte* handler_data = reinterpret_cast<const byte*>
+ (dexGetTryItems(code_item, code_item.tries_size_));
+ return handler_data + offset;
+ }
+
+ // Find the handler associated with a given address, if any.
+ // Initializes the given iterator and returns true if a match is
+ // found. Returns end if there is no applicable handler.
+ CatchHandlerIterator dexFindCatchHandler(const CodeItem& code_item, uint32_t address) const {
+ CatchHandlerItem handler;
+ handler.address_ = -1;
+ int32_t offset = -1;
+
+ // Short-circuit the overwhelmingly common cases.
+ switch (code_item.tries_size_) {
+ case 0:
+ break;
+ case 1: {
+ const TryItem* tries = dexGetTryItems(code_item, 0);
+ uint32_t start = tries->start_addr_;
+ if (address < start)
+ break;
+
+ uint32_t end = start + tries->insn_count_;
+ if (address >= end)
+ break;
+
+ offset = tries->handler_off_;
+ break;
+ }
+ default:
+ offset = dexFindCatchHandlerOffset0(code_item, code_item.tries_size_, address);
+ }
+
+ if (offset >= 0) {
+ const byte* handler_data = dexGetCatchHandlerData(code_item, offset);
+ return CatchHandlerIterator(handler_data);
+ }
+ return CatchHandlerIterator();
+ }
+
+ int32_t dexFindCatchHandlerOffset0(const CodeItem &code_item,
+ int32_t tries_size,
+ uint32_t address) const {
+ // Note: Signed type is important for max and min.
+ int32_t min = 0;
+ int32_t max = tries_size - 1;
+
+ while (max >= min) {
+ int32_t guess = (min + max) >> 1;
+ const TryItem* pTry = dexGetTryItems(code_item, guess);
+ uint32_t start = pTry->start_addr_;
+
+ if (address < start) {
+ max = guess - 1;
+ continue;
+ }
+
+ uint32_t end = start + pTry->insn_count_;
+ if (address >= end) {
+ min = guess + 1;
+ continue;
+ }
+
+ // We have a winner!
+ return (int32_t) pTry->handler_off_;
+ }
+
+ // No match.
+ return -1;
+ }
+
// TODO: const reference
uint32_t dexGetIndexForClassDef(const ClassDef* class_def) const {
diff --git a/src/exception_test.cc b/src/exception_test.cc
new file mode 100644
index 0000000..ae1546f
--- /dev/null
+++ b/src/exception_test.cc
@@ -0,0 +1,105 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include <sys/mman.h>
+
+#include "assembler.h"
+#include "class_linker.h"
+#include "common_test.h"
+#include "dex_file.h"
+#include "jni_compiler.h"
+#include "runtime.h"
+#include "thread.h"
+#include "gtest/gtest.h"
+
+namespace art {
+
+// package java.lang;
+// import java.io.IOException;
+// class Object {};
+// public class MyClass {
+// int f() throws Exception {
+// try {
+// g(1);
+// } catch (IOException e) {
+// return 1;
+// } catch (Exception e) {
+// return 2;
+// }
+// try {
+// g(2);
+// } catch (IOException e) {
+// return 3;
+// }
+// return 0;
+// }
+// void g(int doThrow) throws Exception {
+// if (doThrow == 1)
+// throw new Exception();
+// else if (doThrow == 2)
+// throw new IOException();
+// }
+// }
+
+static const char kMyClassExceptionHandleDex[] =
+ "ZGV4CjAzNQC/bXXtLZJLN1GzLr+ncrvPSl70n8t0yAjgAwAAcAAAAHhWNBIAAAAAAAAAACgDAAAN"
+ "AAAAcAAAAAcAAACkAAAAAwAAAMAAAAAAAAAAAAAAAAYAAADkAAAAAgAAABQBAACMAgAAVAEAAD4C"
+ "AABGAgAASQIAAGUCAAB8AgAAkwIAAKgCAAC8AgAAygIAAM0CAADRAgAA1AIAANcCAAABAAAAAgAA"
+ "AAMAAAAEAAAABQAAAAYAAAAIAAAAAQAAAAAAAAAAAAAACAAAAAYAAAAAAAAACQAAAAYAAAA4AgAA"
+ "AgABAAAAAAADAAEAAAAAAAQAAQAAAAAABAAAAAoAAAAEAAIACwAAAAUAAQAAAAAABQAAAAAAAAD/"
+ "////AAAAAAcAAAAAAAAACQMAAAAAAAAEAAAAAQAAAAUAAAAAAAAABwAAABgCAAATAwAAAAAAAAEA"
+ "AAABAwAAAQABAAAAAADeAgAAAQAAAA4AAAABAAEAAQAAAOMCAAAEAAAAcBAFAAAADgAEAAEAAgAC"
+ "AOgCAAAVAAAAEiISERIQbiAEAAMAEiBuIAQAAwASAA8ADQABECj9DQABICj6DQASMCj3AAADAAAA"
+ "AwABAAcAAAADAAYAAgICDAMPAQISAAAAAwACAAEAAAD3AgAAEwAAABIQMwIIACIAAwBwEAEAAAAn"
+ "ABIgMwIIACIAAgBwEAAAAAAnAA4AAAAAAAAAAAAAAAIAAAAAAAAAAwAAAFQBAAAEAAAAVAEAAAEA"
+ "AAAAAAY8aW5pdD4AAUkAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ABVMamF2YS9pby9JT0V4"
+ "Y2VwdGlvbjsAFUxqYXZhL2xhbmcvRXhjZXB0aW9uOwATTGphdmEvbGFuZy9NeUNsYXNzOwASTGph"
+ "dmEvbGFuZy9PYmplY3Q7AAxNeUNsYXNzLmphdmEAAVYAAlZJAAFmAAFnAAV2YWx1ZQADAAcOAAQA"
+ "Bw4ABwAHLFFOAnYsLR4tIR4AFQEABw48aTxpAAIBAQwcARgDAAABAAWAgATcAgAAAQICgYAE8AID"
+ "AIgDAQDgAwAAAA8AAAAAAAAAAQAAAAAAAAABAAAADQAAAHAAAAACAAAABwAAAKQAAAADAAAAAwAA"
+ "AMAAAAAFAAAABgAAAOQAAAAGAAAAAgAAABQBAAADEAAAAQAAAFQBAAABIAAABAAAAFwBAAAGIAAA"
+ "AQAAABgCAAABEAAAAQAAADgCAAACIAAADQAAAD4CAAADIAAABAAAAN4CAAAEIAAAAQAAAAEDAAAA"
+ "IAAAAgAAAAkDAAAAEAAAAQAAACgDAAA=";
+
+class ExceptionTest : public RuntimeTest {
+};
+
+TEST_F(ExceptionTest, MyClass_F_G) {
+ scoped_ptr<DexFile> dex(OpenDexFileBase64(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_);
+
+ ASSERT_TRUE(code_item != NULL);
+
+ ASSERT_EQ(2u, code_item->tries_size_);
+ 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);
+ 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_));
+ iter.Next();
+ ASSERT_EQ(false, iter.End());
+ EXPECT_STREQ("Ljava/lang/Exception;", dex_file.dexStringByTypeIdx(iter.Get().type_idx_));
+ iter.Next();
+ ASSERT_EQ(true, iter.End());
+
+ 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.Next();
+ ASSERT_EQ(true, iter.End());
+}
+
+} // namespace art
diff --git a/src/object.h b/src/object.h
index e071b97..63f5b16 100644
--- a/src/object.h
+++ b/src/object.h
@@ -5,6 +5,7 @@
#include "constants.h"
#include "casts.h"
+#include "dex_file.h"
#include "globals.h"
#include "heap.h"
#include "logging.h"
@@ -512,12 +513,12 @@
// Method prototype descriptor string (return and argument types).
uint32_t proto_idx_;
+ // Offset to the CodeItem.
+ uint32_t code_off_;
+
// The short-form method descriptor string.
StringPiece shorty_;
- // A pointer to the memory-mapped DEX code.
- const uint16_t* insns_;
-
private:
// Compiled code associated with this method
const void* code_;