Merge "Allow early abort logging"
diff --git a/benchmark/jobject-benchmark/jobject_benchmark.cc b/benchmark/jobject-benchmark/jobject_benchmark.cc
index de43f73..7e0a536 100644
--- a/benchmark/jobject-benchmark/jobject_benchmark.cc
+++ b/benchmark/jobject-benchmark/jobject_benchmark.cc
@@ -29,7 +29,7 @@
   ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(jobj);
   CHECK(obj != nullptr);
   for (jint i = 0; i < reps; ++i) {
-    jobject ref = soa.Env()->AddLocalReference<jobject>(obj.Ptr());
+    jobject ref = soa.Env()->AddLocalReference<jobject>(obj);
     soa.Env()->DeleteLocalRef(ref);
   }
 }
@@ -39,7 +39,7 @@
   ScopedObjectAccess soa(env);
   ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(jobj);
   CHECK(obj != nullptr);
-  jobject ref = soa.Env()->AddLocalReference<jobject>(obj.Ptr());
+  jobject ref = soa.Env()->AddLocalReference<jobject>(obj);
   for (jint i = 0; i < reps; ++i) {
     CHECK_EQ(soa.Decode<mirror::Object>(ref), obj);
   }
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index dca290c..6b56fe0 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -392,11 +392,13 @@
 // -- TODO: We can support (1) if we remove the mutator lock assert during stub lookup.
 # define JNI_TEST_NORMAL_ONLY(TestName)          \
   TEST_F(JniCompilerTest, TestName ## NormalCompiler) { \
+    ScopedCheckHandleScope top_handle_scope_check;  \
     SCOPED_TRACE("Normal JNI with compiler");    \
     gCurrentJni = static_cast<uint32_t>(JniKind::kNormal); \
     TestName ## Impl();                          \
   }                                              \
   TEST_F(JniCompilerTest, TestName ## NormalGeneric) { \
+    ScopedCheckHandleScope top_handle_scope_check;  \
     SCOPED_TRACE("Normal JNI with generic");     \
     gCurrentJni = static_cast<uint32_t>(JniKind::kNormal); \
     TEST_DISABLED_FOR_MIPS();                    \
@@ -408,12 +410,14 @@
 #define JNI_TEST(TestName) \
   JNI_TEST_NORMAL_ONLY(TestName)                 \
   TEST_F(JniCompilerTest, TestName ## FastCompiler) {    \
+    ScopedCheckHandleScope top_handle_scope_check;  \
     SCOPED_TRACE("@FastNative JNI with compiler");  \
     gCurrentJni = static_cast<uint32_t>(JniKind::kFast); \
     TestName ## Impl();                          \
   }                                              \
                                                  \
   TEST_F(JniCompilerTest, TestName ## FastGeneric) { \
+    ScopedCheckHandleScope top_handle_scope_check;  \
     SCOPED_TRACE("@FastNative JNI with generic");  \
     gCurrentJni = static_cast<uint32_t>(JniKind::kFast); \
     TEST_DISABLED_FOR_MIPS();                    \
@@ -424,11 +428,13 @@
 // Test (@CriticalNative) x (compiler, generic) only.
 #define JNI_TEST_CRITICAL_ONLY(TestName) \
   TEST_F(JniCompilerTest, TestName ## CriticalCompiler) { \
+    ScopedCheckHandleScope top_handle_scope_check;  \
     SCOPED_TRACE("@CriticalNative JNI with compiler");  \
     gCurrentJni = static_cast<uint32_t>(JniKind::kCritical); \
     TestName ## Impl();                          \
   }                                              \
   TEST_F(JniCompilerTest, TestName ## CriticalGeneric) { \
+    ScopedCheckHandleScope top_handle_scope_check;  \
     SCOPED_TRACE("@CriticalNative JNI with generic");  \
     gCurrentJni = static_cast<uint32_t>(JniKind::kCritical); \
     SetCheckGenericJni(true);                    \
@@ -515,6 +521,21 @@
 
 bool ScopedDisableCheckNumStackReferences::sCheckNumStackReferences = true;
 
+// Check that the handle scope at the start of this block is the same as the handle scope at the end of the block.
+struct ScopedCheckHandleScope {
+  ScopedCheckHandleScope() {
+    handle_scope_ = Thread::Current()->GetTopHandleScope();
+  }
+
+  ~ScopedCheckHandleScope() {
+    EXPECT_EQ(handle_scope_, Thread::Current()->GetTopHandleScope())
+        << "Top-most handle scope must be the same after all the JNI "
+        << "invocations have finished (as before they were invoked).";
+  }
+
+  HandleScope* handle_scope_;
+};
+
 static void expectNumStackReferences(size_t val1, size_t val2) {
   // In rare cases when JNI functions call themselves recursively,
   // disable this test because it will have a false negative.
diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp
index 0987df7..b9266f7 100644
--- a/dexlayout/Android.bp
+++ b/dexlayout/Android.bp
@@ -21,6 +21,7 @@
         "dex_ir.cc",
         "dex_ir_builder.cc",
         "dex_visualize.cc",
+        "dex_writer.cc",
     ],
     cflags: ["-Wall"],
     shared_libs: [
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
index aff03cd..bc909c3 100644
--- a/dexlayout/dex_ir.cc
+++ b/dexlayout/dex_ir.cc
@@ -55,6 +55,54 @@
                     entry.end_address_, entry.reg_)));
 }
 
+static uint32_t GetDebugInfoStreamSize(const uint8_t* debug_info_stream) {
+  const uint8_t* stream = debug_info_stream;
+  DecodeUnsignedLeb128(&stream);  // line_start
+  uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
+  for (uint32_t i = 0; i < parameters_size; ++i) {
+    DecodeUnsignedLeb128P1(&stream);  // Parameter name.
+  }
+
+  for (;;)  {
+    uint8_t opcode = *stream++;
+    switch (opcode) {
+      case DexFile::DBG_END_SEQUENCE:
+        return stream - debug_info_stream;  // end of stream.
+      case DexFile::DBG_ADVANCE_PC:
+        DecodeUnsignedLeb128(&stream);  // addr_diff
+        break;
+      case DexFile::DBG_ADVANCE_LINE:
+        DecodeSignedLeb128(&stream);  // line_diff
+        break;
+      case DexFile::DBG_START_LOCAL:
+        DecodeUnsignedLeb128(&stream);  // register_num
+        DecodeUnsignedLeb128P1(&stream);  // name_idx
+        DecodeUnsignedLeb128P1(&stream);  // type_idx
+        break;
+      case DexFile::DBG_START_LOCAL_EXTENDED:
+        DecodeUnsignedLeb128(&stream);  // register_num
+        DecodeUnsignedLeb128P1(&stream);  // name_idx
+        DecodeUnsignedLeb128P1(&stream);  // type_idx
+        DecodeUnsignedLeb128P1(&stream);  // sig_idx
+        break;
+      case DexFile::DBG_END_LOCAL:
+      case DexFile::DBG_RESTART_LOCAL:
+        DecodeUnsignedLeb128(&stream);  // register_num
+        break;
+      case DexFile::DBG_SET_PROLOGUE_END:
+      case DexFile::DBG_SET_EPILOGUE_BEGIN:
+        break;
+      case DexFile::DBG_SET_FILE: {
+        DecodeUnsignedLeb128P1(&stream);  // name_idx
+        break;
+      }
+      default: {
+        break;
+      }
+    }
+  }
+}
+
 EncodedValue* Collections::ReadEncodedValue(const uint8_t** data) {
   const uint8_t encoded_value = *(*data)++;
   const uint8_t type = encoded_value & 0x1f;
@@ -179,7 +227,7 @@
 void Collections::CreateProtoId(const DexFile& dex_file, uint32_t i) {
   const DexFile::ProtoId& disk_proto_id = dex_file.GetProtoId(i);
   const DexFile::TypeList* type_list = dex_file.GetProtoParameters(disk_proto_id);
-  TypeList* parameter_type_list = CreateTypeList(type_list, disk_proto_id.parameters_off_, true);
+  TypeList* parameter_type_list = CreateTypeList(type_list, disk_proto_id.parameters_off_);
 
   ProtoId* proto_id = new ProtoId(GetStringId(disk_proto_id.shorty_idx_),
                                   GetTypeId(disk_proto_id.return_type_idx_),
@@ -210,7 +258,7 @@
   const TypeId* superclass = GetTypeIdOrNullPtr(disk_class_def.superclass_idx_);
 
   const DexFile::TypeList* type_list = dex_file.GetInterfacesList(disk_class_def);
-  TypeList* interfaces_type_list = CreateTypeList(type_list, disk_class_def.interfaces_off_, false);
+  TypeList* interfaces_type_list = CreateTypeList(type_list, disk_class_def.interfaces_off_);
 
   const StringId* source_file = GetStringIdOrNullPtr(disk_class_def.source_file_idx_);
   // Annotations.
@@ -232,9 +280,8 @@
   class_defs_.AddIndexedItem(class_def, ClassDefsOffset() + i * ClassDef::ItemSize(), i);
 }
 
-TypeList* Collections::CreateTypeList(
-    const DexFile::TypeList* dex_type_list, uint32_t offset, bool allow_empty) {
-  if (dex_type_list == nullptr && !allow_empty) {
+TypeList* Collections::CreateTypeList(const DexFile::TypeList* dex_type_list, uint32_t offset) {
+  if (dex_type_list == nullptr) {
     return nullptr;
   }
   // TODO: Create more efficient lookup for existing type lists.
@@ -244,7 +291,7 @@
     }
   }
   TypeIdVector* type_vector = new TypeIdVector();
-  uint32_t size = dex_type_list == nullptr ? 0 : dex_type_list->Size();
+  uint32_t size = dex_type_list->Size();
   for (uint32_t index = 0; index < size; ++index) {
     type_vector->push_back(GetTypeId(dex_type_list->GetTypeItem(index).type_idx_));
   }
@@ -257,6 +304,11 @@
   if (static_data == nullptr) {
     return nullptr;
   }
+  for (std::unique_ptr<EncodedArrayItem>& existing_array_item : EncodedArrayItems()) {
+    if (existing_array_item->GetOffset() == offset) {
+      return existing_array_item.get();
+    }
+  }
   uint32_t size = DecodeUnsignedLeb128(&static_data);
   EncodedValueVector* values = new EncodedValueVector();
   for (uint32_t i = 0; i < size; ++i) {
@@ -270,6 +322,11 @@
 
 AnnotationItem* Collections::CreateAnnotationItem(const DexFile::AnnotationItem* annotation,
                                                   uint32_t offset) {
+  for (std::unique_ptr<AnnotationItem>& existing_annotation_item : AnnotationItems()) {
+    if (existing_annotation_item->GetOffset() == offset) {
+      return existing_annotation_item.get();
+    }
+  }
   uint8_t visibility = annotation->visibility_;
   const uint8_t* annotation_data = annotation->annotation_;
   EncodedValue* encoded_value =
@@ -284,9 +341,14 @@
 
 AnnotationSetItem* Collections::CreateAnnotationSetItem(const DexFile& dex_file,
     const DexFile::AnnotationSetItem& disk_annotations_item, uint32_t offset) {
-  if (disk_annotations_item.size_ == 0) {
+  if (disk_annotations_item.size_ == 0 && offset == 0) {
     return nullptr;
   }
+  for (std::unique_ptr<AnnotationSetItem>& existing_anno_set_item : AnnotationSetItems()) {
+    if (existing_anno_set_item->GetOffset() == offset) {
+      return existing_anno_set_item.get();
+    }
+  }
   std::vector<AnnotationItem*>* items = new std::vector<AnnotationItem*>();
   for (uint32_t i = 0; i < disk_annotations_item.size_; ++i) {
     const DexFile::AnnotationItem* annotation =
@@ -305,6 +367,11 @@
 
 AnnotationsDirectoryItem* Collections::CreateAnnotationsDirectoryItem(const DexFile& dex_file,
     const DexFile::AnnotationsDirectoryItem* disk_annotations_item, uint32_t offset) {
+  for (std::unique_ptr<AnnotationsDirectoryItem>& anno_dir_item : AnnotationsDirectoryItems()) {
+    if (anno_dir_item->GetOffset() == offset) {
+      return anno_dir_item.get();
+    }
+  }
   const DexFile::AnnotationSetItem* class_set_item =
       dex_file.GetClassAnnotationSet(disk_annotations_item);
   AnnotationSetItem* class_annotation = nullptr;
@@ -367,16 +434,25 @@
 ParameterAnnotation* Collections::GenerateParameterAnnotation(
     const DexFile& dex_file, MethodId* method_id,
     const DexFile::AnnotationSetRefList* annotation_set_ref_list, uint32_t offset) {
-  std::vector<AnnotationSetItem*>* annotations = new std::vector<AnnotationSetItem*>();
-  for (uint32_t i = 0; i < annotation_set_ref_list->size_; ++i) {
-    const DexFile::AnnotationSetItem* annotation_set_item =
-        dex_file.GetSetRefItemItem(&annotation_set_ref_list->list_[i]);
-    uint32_t set_offset = annotation_set_ref_list->list_[i].annotations_off_;
-    annotations->push_back(CreateAnnotationSetItem(dex_file, *annotation_set_item, set_offset));
+  AnnotationSetRefList* set_ref_list = nullptr;
+  for (std::unique_ptr<AnnotationSetRefList>& existing_set_ref_list : AnnotationSetRefLists()) {
+    if (existing_set_ref_list->GetOffset() == offset) {
+      set_ref_list = existing_set_ref_list.get();
+      break;
+    }
   }
-  AnnotationSetRefList* new_ref_list = new AnnotationSetRefList(annotations);
-  annotation_set_ref_lists_.AddItem(new_ref_list, offset);
-  return new ParameterAnnotation(method_id, new_ref_list);
+  if (set_ref_list == nullptr) {
+    std::vector<AnnotationSetItem*>* annotations = new std::vector<AnnotationSetItem*>();
+    for (uint32_t i = 0; i < annotation_set_ref_list->size_; ++i) {
+      const DexFile::AnnotationSetItem* annotation_set_item =
+          dex_file.GetSetRefItemItem(&annotation_set_ref_list->list_[i]);
+      uint32_t set_offset = annotation_set_ref_list->list_[i].annotations_off_;
+      annotations->push_back(CreateAnnotationSetItem(dex_file, *annotation_set_item, set_offset));
+    }
+    set_ref_list = new AnnotationSetRefList(annotations);
+    annotation_set_ref_lists_.AddItem(set_ref_list, offset);
+  }
+  return new ParameterAnnotation(method_id, set_ref_list);
 }
 
 CodeItem* Collections::CreateCodeItem(const DexFile& dex_file,
@@ -390,7 +466,10 @@
   const uint8_t* debug_info_stream = dex_file.GetDebugInfoStream(&disk_code_item);
   DebugInfoItem* debug_info = nullptr;
   if (debug_info_stream != nullptr) {
-    debug_info = new DebugInfoItem();
+    uint32_t debug_info_size = GetDebugInfoStreamSize(debug_info_stream);
+    uint8_t* debug_info_buffer = new uint8_t[debug_info_size];
+    memcpy(debug_info_buffer, debug_info_stream, debug_info_size);
+    debug_info = new DebugInfoItem(debug_info_size, debug_info_buffer);
     debug_info_items_.AddItem(debug_info, disk_code_item.debug_info_off_);
   }
 
@@ -399,26 +478,41 @@
   memcpy(insns, disk_code_item.insns_, insns_size * sizeof(uint16_t));
 
   TryItemVector* tries = nullptr;
+  CatchHandlerVector* handler_list = nullptr;
   if (tries_size > 0) {
     tries = new TryItemVector();
+    handler_list = new CatchHandlerVector();
     for (uint32_t i = 0; i < tries_size; ++i) {
       const DexFile::TryItem* disk_try_item = dex_file.GetTryItems(disk_code_item, i);
       uint32_t start_addr = disk_try_item->start_addr_;
       uint16_t insn_count = disk_try_item->insn_count_;
-      CatchHandlerVector* handlers = new CatchHandlerVector();
-      for (CatchHandlerIterator it(disk_code_item, *disk_try_item); it.HasNext(); it.Next()) {
-        const uint16_t type_index = it.GetHandlerTypeIndex();
-        const TypeId* type_id = GetTypeIdOrNullPtr(type_index);
-        handlers->push_back(std::unique_ptr<const CatchHandler>(
-            new CatchHandler(type_id, it.GetHandlerAddress())));
+      uint16_t handler_off = disk_try_item->handler_off_;
+      const CatchHandler* handlers = nullptr;
+      for (std::unique_ptr<const CatchHandler>& existing_handlers : *handler_list) {
+        if (handler_off == existing_handlers->GetListOffset()) {
+          handlers = existing_handlers.get();
+        }
+      }
+      if (handlers == nullptr) {
+        bool catch_all = false;
+        TypeAddrPairVector* addr_pairs = new TypeAddrPairVector();
+        for (CatchHandlerIterator it(disk_code_item, *disk_try_item); it.HasNext(); it.Next()) {
+          const uint16_t type_index = it.GetHandlerTypeIndex();
+          const TypeId* type_id = GetTypeIdOrNullPtr(type_index);
+          catch_all |= type_id == nullptr;
+          addr_pairs->push_back(std::unique_ptr<const TypeAddrPair>(
+              new TypeAddrPair(type_id, it.GetHandlerAddress())));
+        }
+        handlers = new CatchHandler(catch_all, handler_off, addr_pairs);
+        handler_list->push_back(std::unique_ptr<const CatchHandler>(handlers));
       }
       TryItem* try_item = new TryItem(start_addr, insn_count, handlers);
       tries->push_back(std::unique_ptr<const TryItem>(try_item));
     }
   }
   // TODO: Calculate the size of the code item.
-  CodeItem* code_item =
-      new CodeItem(registers_size, ins_size, outs_size, debug_info, insns_size, insns, tries);
+  CodeItem* code_item = new CodeItem(
+      registers_size, ins_size, outs_size, debug_info, insns_size, insns, tries, handler_list);
   code_items_.AddItem(code_item, offset);
   return code_item;
 }
diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h
index f3d2c90..5e686d3 100644
--- a/dexlayout/dex_ir.h
+++ b/dexlayout/dex_ir.h
@@ -24,6 +24,7 @@
 
 #include "dex_file-inl.h"
 #include "leb128.h"
+#include "utf.h"
 
 namespace art {
 namespace dex_ir {
@@ -137,10 +138,22 @@
   std::vector<std::unique_ptr<FieldId>>& FieldIds() { return field_ids_.Collection(); }
   std::vector<std::unique_ptr<MethodId>>& MethodIds() { return method_ids_.Collection(); }
   std::vector<std::unique_ptr<ClassDef>>& ClassDefs() { return class_defs_.Collection(); }
-
+  std::vector<std::unique_ptr<StringData>>& StringDatas() { return string_datas_.Collection(); }
   std::vector<std::unique_ptr<TypeList>>& TypeLists() { return type_lists_.Collection(); }
   std::vector<std::unique_ptr<EncodedArrayItem>>& EncodedArrayItems()
       { return encoded_array_items_.Collection(); }
+  std::vector<std::unique_ptr<AnnotationItem>>& AnnotationItems()
+      { return annotation_items_.Collection(); }
+  std::vector<std::unique_ptr<AnnotationSetItem>>& AnnotationSetItems()
+      { return annotation_set_items_.Collection(); }
+  std::vector<std::unique_ptr<AnnotationSetRefList>>& AnnotationSetRefLists()
+      { return annotation_set_ref_lists_.Collection(); }
+  std::vector<std::unique_ptr<AnnotationsDirectoryItem>>& AnnotationsDirectoryItems()
+      { return annotations_directory_items_.Collection(); }
+  std::vector<std::unique_ptr<DebugInfoItem>>& DebugInfoItems()
+      { return debug_info_items_.Collection(); }
+  std::vector<std::unique_ptr<CodeItem>>& CodeItems() { return code_items_.Collection(); }
+  std::vector<std::unique_ptr<ClassData>>& ClassDatas() { return class_datas_.Collection(); }
 
   void CreateStringId(const DexFile& dex_file, uint32_t i);
   void CreateTypeId(const DexFile& dex_file, uint32_t i);
@@ -149,7 +162,7 @@
   void CreateMethodId(const DexFile& dex_file, uint32_t i);
   void CreateClassDef(const DexFile& dex_file, uint32_t i);
 
-  TypeList* CreateTypeList(const DexFile::TypeList* type_list, uint32_t offset, bool allow_empty);
+  TypeList* CreateTypeList(const DexFile::TypeList* type_list, uint32_t offset);
   EncodedArrayItem* CreateEncodedArrayItem(const uint8_t* static_data, uint32_t offset);
   AnnotationItem* CreateAnnotationItem(const DexFile::AnnotationItem* annotation, uint32_t offset);
   AnnotationSetItem* CreateAnnotationSetItem(const DexFile& dex_file,
@@ -182,14 +195,16 @@
   uint32_t ClassDefsOffset() const { return class_defs_.GetOffset(); }
   uint32_t StringDatasOffset() const { return string_datas_.GetOffset(); }
   uint32_t TypeListsOffset() const { return type_lists_.GetOffset(); }
-  uint32_t EncodedArrayOffset() const { return encoded_array_items_.GetOffset(); }
-  uint32_t AnnotationOffset() const { return annotation_items_.GetOffset(); }
-  uint32_t AnnotationSetOffset() const { return annotation_set_items_.GetOffset(); }
+  uint32_t EncodedArrayItemsOffset() const { return encoded_array_items_.GetOffset(); }
+  uint32_t AnnotationItemsOffset() const { return annotation_items_.GetOffset(); }
+  uint32_t AnnotationSetItemsOffset() const { return annotation_set_items_.GetOffset(); }
   uint32_t AnnotationSetRefListsOffset() const { return annotation_set_ref_lists_.GetOffset(); }
-  uint32_t AnnotationsDirectoryOffset() const { return annotations_directory_items_.GetOffset(); }
-  uint32_t DebugInfoOffset() const { return debug_info_items_.GetOffset(); }
+  uint32_t AnnotationsDirectoryItemsOffset() const
+      { return annotations_directory_items_.GetOffset(); }
+  uint32_t DebugInfoItemsOffset() const { return debug_info_items_.GetOffset(); }
   uint32_t CodeItemsOffset() const { return code_items_.GetOffset(); }
   uint32_t ClassDatasOffset() const { return class_datas_.GetOffset(); }
+  uint32_t MapItemOffset() const { return map_item_offset_; }
 
   void SetStringIdsOffset(uint32_t new_offset) { string_ids_.SetOffset(new_offset); }
   void SetTypeIdsOffset(uint32_t new_offset) { type_ids_.SetOffset(new_offset); }
@@ -199,16 +214,19 @@
   void SetClassDefsOffset(uint32_t new_offset) { class_defs_.SetOffset(new_offset); }
   void SetStringDatasOffset(uint32_t new_offset) { string_datas_.SetOffset(new_offset); }
   void SetTypeListsOffset(uint32_t new_offset) { type_lists_.SetOffset(new_offset); }
-  void SetEncodedArrayOffset(uint32_t new_offset) { encoded_array_items_.SetOffset(new_offset); }
-  void SetAnnotationOffset(uint32_t new_offset) { annotation_items_.SetOffset(new_offset); }
-  void SetAnnotationSetOffset(uint32_t new_offset) { annotation_set_items_.SetOffset(new_offset); }
+  void SetEncodedArrayItemsOffset(uint32_t new_offset)
+      { encoded_array_items_.SetOffset(new_offset); }
+  void SetAnnotationItemsOffset(uint32_t new_offset) { annotation_items_.SetOffset(new_offset); }
+  void SetAnnotationSetItemsOffset(uint32_t new_offset)
+      { annotation_set_items_.SetOffset(new_offset); }
   void SetAnnotationSetRefListsOffset(uint32_t new_offset)
       { annotation_set_ref_lists_.SetOffset(new_offset); }
-  void SetAnnotationsDirectoryOffset(uint32_t new_offset)
+  void SetAnnotationsDirectoryItemsOffset(uint32_t new_offset)
       { annotations_directory_items_.SetOffset(new_offset); }
-  void SetDebugInfoOffset(uint32_t new_offset) { debug_info_items_.SetOffset(new_offset); }
+  void SetDebugInfoItemsOffset(uint32_t new_offset) { debug_info_items_.SetOffset(new_offset); }
   void SetCodeItemsOffset(uint32_t new_offset) { code_items_.SetOffset(new_offset); }
   void SetClassDatasOffset(uint32_t new_offset) { class_datas_.SetOffset(new_offset); }
+  void SetMapItemOffset(uint32_t new_offset) { map_item_offset_ = new_offset; }
 
   uint32_t StringIdsSize() const { return string_ids_.Size(); }
   uint32_t TypeIdsSize() const { return type_ids_.Size(); }
@@ -216,15 +234,14 @@
   uint32_t FieldIdsSize() const { return field_ids_.Size(); }
   uint32_t MethodIdsSize() const { return method_ids_.Size(); }
   uint32_t ClassDefsSize() const { return class_defs_.Size(); }
-
   uint32_t StringDatasSize() const { return string_datas_.Size(); }
   uint32_t TypeListsSize() const { return type_lists_.Size(); }
-  uint32_t EncodedArraySize() const { return encoded_array_items_.Size(); }
-  uint32_t AnnotationSize() const { return annotation_items_.Size(); }
-  uint32_t AnnotationSetSize() const { return annotation_set_items_.Size(); }
+  uint32_t EncodedArrayItemsSize() const { return encoded_array_items_.Size(); }
+  uint32_t AnnotationItemsSize() const { return annotation_items_.Size(); }
+  uint32_t AnnotationSetItemsSize() const { return annotation_set_items_.Size(); }
   uint32_t AnnotationSetRefListsSize() const { return annotation_set_ref_lists_.Size(); }
-  uint32_t AnnotationsDirectorySize() const { return annotations_directory_items_.Size(); }
-  uint32_t DebugInfoSize() const { return debug_info_items_.Size(); }
+  uint32_t AnnotationsDirectoryItemsSize() const { return annotations_directory_items_.Size(); }
+  uint32_t DebugInfoItemsSize() const { return debug_info_items_.Size(); }
   uint32_t CodeItemsSize() const { return code_items_.Size(); }
   uint32_t ClassDatasSize() const { return class_datas_.Size(); }
 
@@ -255,6 +272,8 @@
   CollectionWithOffset<CodeItem> code_items_;
   CollectionWithOffset<ClassData> class_datas_;
 
+  uint32_t map_item_offset_ = 0;
+
   DISALLOW_COPY_AND_ASSIGN(Collections);
 };
 
@@ -364,7 +383,7 @@
 class StringData : public Item {
  public:
   explicit StringData(const char* data) : data_(strdup(data)) {
-    size_ = UnsignedLeb128Size(strlen(data)) + strlen(data);
+    size_ = UnsignedLeb128Size(CountModifiedUtf8Chars(data)) + strlen(data);
   }
 
   const char* Data() const { return data_.get(); }
@@ -372,7 +391,7 @@
   void Accept(AbstractDispatcher* dispatch) const { dispatch->Dispatch(this); }
 
  private:
-  std::unique_ptr<const char> data_;
+  UniqueCPtr<const char> data_;
 
   DISALLOW_COPY_AND_ASSIGN(StringData);
 };
@@ -442,14 +461,14 @@
 
   const StringId* Shorty() const { return shorty_; }
   const TypeId* ReturnType() const { return return_type_; }
-  const TypeIdVector& Parameters() const { return *parameters_->GetTypeList(); }
+  const TypeList* Parameters() const { return parameters_; }
 
   void Accept(AbstractDispatcher* dispatch) const { dispatch->Dispatch(this); }
 
  private:
   const StringId* shorty_;
   const TypeId* return_type_;
-  TypeList* parameters_;
+  TypeList* parameters_;  // This can be nullptr.
 
   DISALLOW_COPY_AND_ASSIGN(ProtoId);
 };
@@ -533,7 +552,7 @@
  private:
   uint32_t access_flags_;
   const MethodId* method_id_;
-  const CodeItem* code_;
+  const CodeItem* code_;  // This can be nullptr.
 
   DISALLOW_COPY_AND_ASSIGN(MethodItem);
 };
@@ -691,8 +710,8 @@
         interfaces_(interfaces),
         source_file_(source_file),
         annotations_(annotations),
-        static_values_(static_values),
-        class_data_(class_data) { size_ = kClassDefItemSize; }
+        class_data_(class_data),
+        static_values_(static_values) { size_ = kClassDefItemSize; }
 
   ~ClassDef() OVERRIDE { }
 
@@ -706,8 +725,8 @@
   uint32_t InterfacesOffset() { return interfaces_ == nullptr ? 0 : interfaces_->GetOffset(); }
   const StringId* SourceFile() const { return source_file_; }
   AnnotationsDirectoryItem* Annotations() const { return annotations_; }
-  EncodedArrayItem* StaticValues() { return static_values_; }
   ClassData* GetClassData() { return class_data_; }
+  EncodedArrayItem* StaticValues() { return static_values_; }
 
   MethodItem* GenerateMethodItem(Header& header, ClassDataItemIterator& cdii);
 
@@ -716,19 +735,19 @@
  private:
   const TypeId* class_type_;
   uint32_t access_flags_;
-  const TypeId* superclass_;
-  TypeList* interfaces_;
-  const StringId* source_file_;
-  AnnotationsDirectoryItem* annotations_;
-  EncodedArrayItem* static_values_;
-  ClassData* class_data_;
+  const TypeId* superclass_;  // This can be nullptr.
+  TypeList* interfaces_;  // This can be nullptr.
+  const StringId* source_file_;  // This can be nullptr.
+  AnnotationsDirectoryItem* annotations_;  // This can be nullptr.
+  ClassData* class_data_;  // This can be nullptr.
+  EncodedArrayItem* static_values_;  // This can be nullptr.
 
   DISALLOW_COPY_AND_ASSIGN(ClassDef);
 };
 
-class CatchHandler {
+class TypeAddrPair {
  public:
-  CatchHandler(const TypeId* type_id, uint32_t address) : type_id_(type_id), address_(address) { }
+  TypeAddrPair(const TypeId* type_id, uint32_t address) : type_id_(type_id), address_(address) { }
 
   const TypeId* GetTypeId() const { return type_id_; }
   uint32_t GetAddress() const { return address_; }
@@ -737,6 +756,25 @@
   const TypeId* type_id_;
   uint32_t address_;
 
+  DISALLOW_COPY_AND_ASSIGN(TypeAddrPair);
+};
+
+using TypeAddrPairVector = std::vector<std::unique_ptr<const TypeAddrPair>>;
+
+class CatchHandler {
+ public:
+  explicit CatchHandler(bool catch_all, uint16_t list_offset, TypeAddrPairVector* handlers)
+      : catch_all_(catch_all), list_offset_(list_offset), handlers_(handlers) { }
+
+  bool HasCatchAll() const { return catch_all_; }
+  uint16_t GetListOffset() const { return list_offset_; }
+  TypeAddrPairVector* GetHandlers() const { return handlers_.get(); }
+
+ private:
+  bool catch_all_;
+  uint16_t list_offset_;
+  std::unique_ptr<TypeAddrPairVector> handlers_;
+
   DISALLOW_COPY_AND_ASSIGN(CatchHandler);
 };
 
@@ -744,20 +782,20 @@
 
 class TryItem : public Item {
  public:
-  TryItem(uint32_t start_addr, uint16_t insn_count, CatchHandlerVector* handlers)
+  TryItem(uint32_t start_addr, uint16_t insn_count, const CatchHandler* handlers)
       : start_addr_(start_addr), insn_count_(insn_count), handlers_(handlers) { }
   ~TryItem() OVERRIDE { }
 
   uint32_t StartAddr() const { return start_addr_; }
   uint16_t InsnCount() const { return insn_count_; }
-  const CatchHandlerVector& GetHandlers() const { return *handlers_.get(); }
+  const CatchHandler* GetHandlers() const { return handlers_; }
 
   void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
 
  private:
   uint32_t start_addr_;
   uint16_t insn_count_;
-  std::unique_ptr<CatchHandlerVector> handlers_;
+  const CatchHandler* handlers_;
 
   DISALLOW_COPY_AND_ASSIGN(TryItem);
 };
@@ -772,14 +810,16 @@
            DebugInfoItem* debug_info,
            uint32_t insns_size,
            uint16_t* insns,
-           TryItemVector* tries)
+           TryItemVector* tries,
+           CatchHandlerVector* handlers)
       : registers_size_(registers_size),
         ins_size_(ins_size),
         outs_size_(outs_size),
         debug_info_(debug_info),
         insns_size_(insns_size),
         insns_(insns),
-        tries_(tries) { }
+        tries_(tries),
+        handlers_(handlers) { }
 
   ~CodeItem() OVERRIDE { }
 
@@ -791,6 +831,7 @@
   uint32_t InsnsSize() const { return insns_size_; }
   uint16_t* Insns() const { return insns_.get(); }
   TryItemVector* Tries() const { return tries_.get(); }
+  CatchHandlerVector* Handlers() const { return handlers_.get(); }
 
   void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
 
@@ -798,10 +839,11 @@
   uint16_t registers_size_;
   uint16_t ins_size_;
   uint16_t outs_size_;
-  DebugInfoItem* debug_info_;
+  DebugInfoItem* debug_info_;  // This can be nullptr.
   uint32_t insns_size_;
   std::unique_ptr<uint16_t[]> insns_;
-  std::unique_ptr<TryItemVector> tries_;
+  std::unique_ptr<TryItemVector> tries_;  // This can be nullptr.
+  std::unique_ptr<CatchHandlerVector> handlers_;  // This can be nullptr.
 
   DISALLOW_COPY_AND_ASSIGN(CodeItem);
 };
@@ -841,12 +883,19 @@
 
 class DebugInfoItem : public Item {
  public:
-  DebugInfoItem() = default;
+  DebugInfoItem(uint32_t debug_info_size, uint8_t* debug_info)
+     : debug_info_size_(debug_info_size), debug_info_(debug_info) { }
+
+  uint32_t GetDebugInfoSize() const { return debug_info_size_; }
+  uint8_t* GetDebugInfo() const { return debug_info_.get(); }
 
   PositionInfoVector& GetPositionInfo() { return positions_; }
   LocalInfoVector& GetLocalInfo() { return locals_; }
 
  private:
+  uint32_t debug_info_size_;
+  std::unique_ptr<uint8_t[]> debug_info_;
+
   PositionInfoVector positions_;
   LocalInfoVector locals_;
 
@@ -899,7 +948,7 @@
   void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
 
  private:
-  std::unique_ptr<std::vector<AnnotationSetItem*>> items_;
+  std::unique_ptr<std::vector<AnnotationSetItem*>> items_;  // Elements of vector can be nullptr.
 
   DISALLOW_COPY_AND_ASSIGN(AnnotationSetRefList);
 };
@@ -974,10 +1023,10 @@
   void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
 
  private:
-  AnnotationSetItem* class_annotation_;
-  std::unique_ptr<FieldAnnotationVector> field_annotations_;
-  std::unique_ptr<MethodAnnotationVector> method_annotations_;
-  std::unique_ptr<ParameterAnnotationVector> parameter_annotations_;
+  AnnotationSetItem* class_annotation_;  // This can be nullptr.
+  std::unique_ptr<FieldAnnotationVector> field_annotations_;  // This can be nullptr.
+  std::unique_ptr<MethodAnnotationVector> method_annotations_;  // This can be nullptr.
+  std::unique_ptr<ParameterAnnotationVector> parameter_annotations_;  // This can be nullptr.
 
   DISALLOW_COPY_AND_ASSIGN(AnnotationsDirectoryItem);
 };
diff --git a/dexlayout/dex_ir_builder.cc b/dexlayout/dex_ir_builder.cc
index 599f48b..68ff2a2 100644
--- a/dexlayout/dex_ir_builder.cc
+++ b/dexlayout/dex_ir_builder.cc
@@ -70,6 +70,8 @@
   for (uint32_t i = 0; i < dex_file.NumClassDefs(); ++i) {
     collections.CreateClassDef(dex_file, i);
   }
+  // MapItem.
+  collections.SetMapItemOffset(disk_header.map_off_);
 
   CheckAndSetRemainingOffsets(dex_file, &collections);
 
@@ -124,7 +126,7 @@
         collections->SetAnnotationSetRefListsOffset(item->offset_);
         break;
       case DexFile::kDexTypeAnnotationSetItem:
-        collections->SetAnnotationSetOffset(item->offset_);
+        collections->SetAnnotationSetItemsOffset(item->offset_);
         break;
       case DexFile::kDexTypeClassDataItem:
         collections->SetClassDatasOffset(item->offset_);
@@ -136,16 +138,16 @@
         collections->SetStringDatasOffset(item->offset_);
         break;
       case DexFile::kDexTypeDebugInfoItem:
-        collections->SetDebugInfoOffset(item->offset_);
+        collections->SetDebugInfoItemsOffset(item->offset_);
         break;
       case DexFile::kDexTypeAnnotationItem:
-        collections->SetAnnotationOffset(item->offset_);
+        collections->SetAnnotationItemsOffset(item->offset_);
         break;
       case DexFile::kDexTypeEncodedArrayItem:
-        collections->SetEncodedArrayOffset(item->offset_);
+        collections->SetEncodedArrayItemsOffset(item->offset_);
         break;
       case DexFile::kDexTypeAnnotationsDirectoryItem:
-        collections->SetAnnotationsDirectoryOffset(item->offset_);
+        collections->SetAnnotationsDirectoryItemsOffset(item->offset_);
         break;
       default:
         LOG(ERROR) << "Unknown map list item type.";
diff --git a/dexlayout/dex_visualize.cc b/dexlayout/dex_visualize.cc
index be7bade..46dff5f 100644
--- a/dexlayout/dex_visualize.cc
+++ b/dexlayout/dex_visualize.cc
@@ -87,18 +87,18 @@
   }, {
     "EncArr",
     DexFile::kDexTypeEncodedArrayItem,
-    &dex_ir::Collections::EncodedArraySize,
-    &dex_ir::Collections::EncodedArrayOffset
+    &dex_ir::Collections::EncodedArrayItemsSize,
+    &dex_ir::Collections::EncodedArrayItemsOffset
   }, {
     "Annotation",
     DexFile::kDexTypeAnnotationItem,
-    &dex_ir::Collections::AnnotationSize,
-    &dex_ir::Collections::AnnotationOffset
+    &dex_ir::Collections::AnnotationItemsSize,
+    &dex_ir::Collections::AnnotationItemsOffset
   }, {
     "AnnoSet",
     DexFile::kDexTypeAnnotationSetItem,
-    &dex_ir::Collections::AnnotationSetSize,
-    &dex_ir::Collections::AnnotationSetOffset
+    &dex_ir::Collections::AnnotationSetItemsSize,
+    &dex_ir::Collections::AnnotationSetItemsOffset
   }, {
     "AnnoSetRL",
     DexFile::kDexTypeAnnotationSetRefList,
@@ -107,13 +107,13 @@
   }, {
     "AnnoDir",
     DexFile::kDexTypeAnnotationsDirectoryItem,
-    &dex_ir::Collections::AnnotationsDirectorySize,
-    &dex_ir::Collections::AnnotationsDirectoryOffset
+    &dex_ir::Collections::AnnotationsDirectoryItemsSize,
+    &dex_ir::Collections::AnnotationsDirectoryItemsOffset
   }, {
     "DebugInfo",
     DexFile::kDexTypeDebugInfoItem,
-    &dex_ir::Collections::DebugInfoSize,
-    &dex_ir::Collections::DebugInfoOffset
+    &dex_ir::Collections::DebugInfoItemsSize,
+    &dex_ir::Collections::DebugInfoItemsOffset
   }, {
     "CodeItem",
     DexFile::kDexTypeCodeItem,
@@ -244,9 +244,11 @@
       return;
     }
     DumpStringId(proto_id->Shorty(), class_index);
-    const dex_ir::TypeIdVector& parameters = proto_id->Parameters();
-    for (const dex_ir::TypeId* t : parameters) {
-      DumpTypeId(t, class_index);
+    const dex_ir::TypeList* type_list = proto_id->Parameters();
+    if (type_list != nullptr) {
+      for (const dex_ir::TypeId* t : *type_list->GetTypeList()) {
+        DumpTypeId(t, class_index);
+      }
     }
     DumpTypeId(proto_id->ReturnType(), class_index);
   }
diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc
new file mode 100644
index 0000000..dba5da0
--- /dev/null
+++ b/dexlayout/dex_writer.cc
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Header file of an in-memory representation of DEX files.
+ */
+
+#include <stdint.h>
+
+#include <queue>
+#include <vector>
+
+#include "dex_writer.h"
+#include "utf.h"
+
+namespace art {
+
+size_t EncodeIntValue(int32_t value, uint8_t* buffer) {
+  size_t length = 0;
+  if (value >= 0) {
+    while (value > 0x7f) {
+      buffer[length++] = static_cast<uint8_t>(value);
+      value >>= 8;
+    }
+  } else {
+    while (value < -0x80) {
+      buffer[length++] = static_cast<uint8_t>(value);
+      value >>= 8;
+    }
+  }
+  buffer[length++] = static_cast<uint8_t>(value);
+  return length;
+}
+
+size_t EncodeUIntValue(uint32_t value, uint8_t* buffer) {
+  size_t length = 0;
+  do {
+    buffer[length++] = static_cast<uint8_t>(value);
+    value >>= 8;
+  } while (value != 0);
+  return length;
+}
+
+size_t EncodeLongValue(int64_t value, uint8_t* buffer) {
+  size_t length = 0;
+  if (value >= 0) {
+    while (value > 0x7f) {
+      buffer[length++] = static_cast<uint8_t>(value);
+      value >>= 8;
+    }
+  } else {
+    while (value < -0x80) {
+      buffer[length++] = static_cast<uint8_t>(value);
+      value >>= 8;
+    }
+  }
+  buffer[length++] = static_cast<uint8_t>(value);
+  return length;
+}
+
+union FloatUnion {
+  float f_;
+  uint32_t i_;
+};
+
+size_t EncodeFloatValue(float value, uint8_t* buffer) {
+  FloatUnion float_union;
+  float_union.f_ = value;
+  uint32_t int_value = float_union.i_;
+  size_t index = 3;
+  do {
+    buffer[index--] = int_value >> 24;
+    int_value <<= 8;
+  } while (int_value != 0);
+  return 3 - index;
+}
+
+union DoubleUnion {
+  double d_;
+  uint64_t l_;
+};
+
+size_t EncodeDoubleValue(double value, uint8_t* buffer) {
+  DoubleUnion double_union;
+  double_union.d_ = value;
+  uint64_t long_value = double_union.l_;
+  size_t index = 7;
+  do {
+    buffer[index--] = long_value >> 56;
+    long_value <<= 8;
+  } while (long_value != 0);
+  return 7 - index;
+}
+
+size_t DexWriter::Write(const void* buffer, size_t length, size_t offset) {
+  return dex_file_->PwriteFully(buffer, length, offset) ? length : 0;
+}
+
+size_t DexWriter::WriteSleb128(uint32_t value, size_t offset) {
+  uint8_t buffer[8];
+  EncodeSignedLeb128(buffer, value);
+  return Write(buffer, SignedLeb128Size(value), offset);
+}
+
+size_t DexWriter::WriteUleb128(uint32_t value, size_t offset) {
+  uint8_t buffer[8];
+  EncodeUnsignedLeb128(buffer, value);
+  return Write(buffer, UnsignedLeb128Size(value), offset);
+}
+
+size_t DexWriter::WriteEncodedValue(dex_ir::EncodedValue* encoded_value, size_t offset) {
+  size_t original_offset = offset;
+  size_t start = 0;
+  size_t length;
+  uint8_t buffer[8];
+  int8_t type = encoded_value->Type();
+  switch (type) {
+    case DexFile::kDexAnnotationByte:
+      length = EncodeIntValue(encoded_value->GetByte(), buffer);
+      break;
+    case DexFile::kDexAnnotationShort:
+      length = EncodeIntValue(encoded_value->GetShort(), buffer);
+      break;
+    case DexFile::kDexAnnotationChar:
+      length = EncodeUIntValue(encoded_value->GetChar(), buffer);
+      break;
+    case DexFile::kDexAnnotationInt:
+      length = EncodeIntValue(encoded_value->GetInt(), buffer);
+      break;
+    case DexFile::kDexAnnotationLong:
+      length = EncodeLongValue(encoded_value->GetLong(), buffer);
+      break;
+    case DexFile::kDexAnnotationFloat:
+      length = EncodeFloatValue(encoded_value->GetFloat(), buffer);
+      start = 4 - length;
+      break;
+    case DexFile::kDexAnnotationDouble:
+      length = EncodeDoubleValue(encoded_value->GetDouble(), buffer);
+      start = 8 - length;
+      break;
+    case DexFile::kDexAnnotationString:
+      length = EncodeUIntValue(encoded_value->GetStringId()->GetIndex(), buffer);
+      break;
+    case DexFile::kDexAnnotationType:
+      length = EncodeUIntValue(encoded_value->GetTypeId()->GetIndex(), buffer);
+      break;
+    case DexFile::kDexAnnotationField:
+    case DexFile::kDexAnnotationEnum:
+      length = EncodeUIntValue(encoded_value->GetFieldId()->GetIndex(), buffer);
+      break;
+    case DexFile::kDexAnnotationMethod:
+      length = EncodeUIntValue(encoded_value->GetMethodId()->GetIndex(), buffer);
+      break;
+    case DexFile::kDexAnnotationArray:
+      offset += WriteEncodedValueHeader(type, 0, offset);
+      offset += WriteEncodedArray(encoded_value->GetEncodedArray()->GetEncodedValues(), offset);
+      return offset - original_offset;
+    case DexFile::kDexAnnotationAnnotation:
+      offset += WriteEncodedValueHeader(type, 0, offset);
+      offset += WriteEncodedAnnotation(encoded_value->GetEncodedAnnotation(), offset);
+      return offset - original_offset;
+    case DexFile::kDexAnnotationNull:
+      return WriteEncodedValueHeader(type, 0, offset);
+    case DexFile::kDexAnnotationBoolean:
+      return WriteEncodedValueHeader(type, encoded_value->GetBoolean() ? 1 : 0, offset);
+    default:
+      return 0;
+  }
+  offset += WriteEncodedValueHeader(type, length - 1, offset);
+  offset += Write(buffer + start, length, offset);
+  return offset - original_offset;
+}
+
+size_t DexWriter::WriteEncodedValueHeader(int8_t value_type, size_t value_arg, size_t offset) {
+  uint8_t buffer[1] = { static_cast<uint8_t>((value_arg << 5) | value_type) };
+  return Write(buffer, sizeof(uint8_t), offset);
+}
+
+size_t DexWriter::WriteEncodedArray(dex_ir::EncodedValueVector* values, size_t offset) {
+  size_t original_offset = offset;
+  offset += WriteUleb128(values->size(), offset);
+  for (std::unique_ptr<dex_ir::EncodedValue>& value : *values) {
+    offset += WriteEncodedValue(value.get(), offset);
+  }
+  return offset - original_offset;
+}
+
+size_t DexWriter::WriteEncodedAnnotation(dex_ir::EncodedAnnotation* annotation, size_t offset) {
+  size_t original_offset = offset;
+  offset += WriteUleb128(annotation->GetType()->GetIndex(), offset);
+  offset += WriteUleb128(annotation->GetAnnotationElements()->size(), offset);
+  for (std::unique_ptr<dex_ir::AnnotationElement>& annotation_element :
+      *annotation->GetAnnotationElements()) {
+    offset += WriteUleb128(annotation_element->GetName()->GetIndex(), offset);
+    offset += WriteEncodedValue(annotation_element->GetValue(), offset);
+  }
+  return offset - original_offset;
+}
+
+size_t DexWriter::WriteEncodedFields(dex_ir::FieldItemVector* fields, size_t offset) {
+  size_t original_offset = offset;
+  uint32_t prev_index = 0;
+  for (std::unique_ptr<dex_ir::FieldItem>& field : *fields) {
+    uint32_t index = field->GetFieldId()->GetIndex();
+    offset += WriteUleb128(index - prev_index, offset);
+    offset += WriteUleb128(field->GetAccessFlags(), offset);
+    prev_index = index;
+  }
+  return offset - original_offset;
+}
+
+size_t DexWriter::WriteEncodedMethods(dex_ir::MethodItemVector* methods, size_t offset) {
+  size_t original_offset = offset;
+  uint32_t prev_index = 0;
+  for (std::unique_ptr<dex_ir::MethodItem>& method : *methods) {
+    uint32_t index = method->GetMethodId()->GetIndex();
+    uint32_t code_off = method->GetCodeItem() == nullptr ? 0 : method->GetCodeItem()->GetOffset();
+    offset += WriteUleb128(index - prev_index, offset);
+    offset += WriteUleb128(method->GetAccessFlags(), offset);
+    offset += WriteUleb128(code_off, offset);
+    prev_index = index;
+  }
+  return offset - original_offset;
+}
+
+void DexWriter::WriteStrings() {
+  uint32_t string_data_off[1];
+  for (std::unique_ptr<dex_ir::StringId>& string_id : header_.GetCollections().StringIds()) {
+    string_data_off[0] = string_id->DataItem()->GetOffset();
+    Write(string_data_off, string_id->GetSize(), string_id->GetOffset());
+  }
+
+  for (std::unique_ptr<dex_ir::StringData>& string_data : header_.GetCollections().StringDatas()) {
+    uint32_t offset = string_data->GetOffset();
+    offset += WriteUleb128(CountModifiedUtf8Chars(string_data->Data()), offset);
+    Write(string_data->Data(), strlen(string_data->Data()), offset);
+  }
+}
+
+void DexWriter::WriteTypes() {
+  uint32_t descriptor_idx[1];
+  for (std::unique_ptr<dex_ir::TypeId>& type_id : header_.GetCollections().TypeIds()) {
+    descriptor_idx[0] = type_id->GetStringId()->GetIndex();
+    Write(descriptor_idx, type_id->GetSize(), type_id->GetOffset());
+  }
+}
+
+void DexWriter::WriteTypeLists() {
+  uint32_t size[1];
+  uint16_t list[1];
+  for (std::unique_ptr<dex_ir::TypeList>& type_list : header_.GetCollections().TypeLists()) {
+    size[0] = type_list->GetTypeList()->size();
+    uint32_t offset = type_list->GetOffset();
+    offset += Write(size, sizeof(uint32_t), offset);
+    for (const dex_ir::TypeId* type_id : *type_list->GetTypeList()) {
+      list[0] = type_id->GetIndex();
+      offset += Write(list, sizeof(uint16_t), offset);
+    }
+  }
+}
+
+void DexWriter::WriteProtos() {
+  uint32_t buffer[3];
+  for (std::unique_ptr<dex_ir::ProtoId>& proto_id : header_.GetCollections().ProtoIds()) {
+    buffer[0] = proto_id->Shorty()->GetIndex();
+    buffer[1] = proto_id->ReturnType()->GetIndex();
+    buffer[2] = proto_id->Parameters() == nullptr ? 0 : proto_id->Parameters()->GetOffset();
+    Write(buffer, proto_id->GetSize(), proto_id->GetOffset());
+  }
+}
+
+void DexWriter::WriteFields() {
+  uint16_t buffer[4];
+  for (std::unique_ptr<dex_ir::FieldId>& field_id : header_.GetCollections().FieldIds()) {
+    buffer[0] = field_id->Class()->GetIndex();
+    buffer[1] = field_id->Type()->GetIndex();
+    buffer[2] = field_id->Name()->GetIndex();
+    buffer[3] = field_id->Name()->GetIndex() >> 16;
+    Write(buffer, field_id->GetSize(), field_id->GetOffset());
+  }
+}
+
+void DexWriter::WriteMethods() {
+  uint16_t buffer[4];
+  for (std::unique_ptr<dex_ir::MethodId>& method_id : header_.GetCollections().MethodIds()) {
+    buffer[0] = method_id->Class()->GetIndex();
+    buffer[1] = method_id->Proto()->GetIndex();
+    buffer[2] = method_id->Name()->GetIndex();
+    buffer[3] = method_id->Name()->GetIndex() >> 16;
+    Write(buffer, method_id->GetSize(), method_id->GetOffset());
+  }
+}
+
+void DexWriter::WriteEncodedArrays() {
+  for (std::unique_ptr<dex_ir::EncodedArrayItem>& encoded_array :
+      header_.GetCollections().EncodedArrayItems()) {
+    WriteEncodedArray(encoded_array->GetEncodedValues(), encoded_array->GetOffset());
+  }
+}
+
+void DexWriter::WriteAnnotations() {
+  uint8_t visibility[1];
+  for (std::unique_ptr<dex_ir::AnnotationItem>& annotation :
+      header_.GetCollections().AnnotationItems()) {
+    visibility[0] = annotation->GetVisibility();
+    size_t offset = annotation->GetOffset();
+    offset += Write(visibility, sizeof(uint8_t), offset);
+    WriteEncodedAnnotation(annotation->GetAnnotation(), offset);
+  }
+}
+
+void DexWriter::WriteAnnotationSets() {
+  uint32_t size[1];
+  uint32_t annotation_off[1];
+  for (std::unique_ptr<dex_ir::AnnotationSetItem>& annotation_set :
+      header_.GetCollections().AnnotationSetItems()) {
+    size[0] = annotation_set->GetItems()->size();
+    size_t offset = annotation_set->GetOffset();
+    offset += Write(size, sizeof(uint32_t), offset);
+    for (dex_ir::AnnotationItem* annotation : *annotation_set->GetItems()) {
+      annotation_off[0] = annotation->GetOffset();
+      offset += Write(annotation_off, sizeof(uint32_t), offset);
+    }
+  }
+}
+
+void DexWriter::WriteAnnotationSetRefs() {
+  uint32_t size[1];
+  uint32_t annotations_off[1];
+  for (std::unique_ptr<dex_ir::AnnotationSetRefList>& annotation_set_ref :
+        header_.GetCollections().AnnotationSetRefLists()) {
+    size[0] = annotation_set_ref->GetItems()->size();
+    size_t offset = annotation_set_ref->GetOffset();
+    offset += Write(size, sizeof(uint32_t), offset);
+    for (dex_ir::AnnotationSetItem* annotation_set : *annotation_set_ref->GetItems()) {
+      annotations_off[0] = annotation_set == nullptr ? 0 : annotation_set->GetOffset();
+      offset += Write(annotations_off, sizeof(uint32_t), offset);
+    }
+  }
+}
+
+void DexWriter::WriteAnnotationsDirectories() {
+  uint32_t directory_buffer[4];
+  uint32_t annotation_buffer[2];
+  for (std::unique_ptr<dex_ir::AnnotationsDirectoryItem>& annotations_directory :
+          header_.GetCollections().AnnotationsDirectoryItems()) {
+    directory_buffer[0] = annotations_directory->GetClassAnnotation() == nullptr ? 0 :
+        annotations_directory->GetClassAnnotation()->GetOffset();
+    directory_buffer[1] = annotations_directory->GetFieldAnnotations() == nullptr ? 0 :
+        annotations_directory->GetFieldAnnotations()->size();
+    directory_buffer[2] = annotations_directory->GetMethodAnnotations() == nullptr ? 0 :
+        annotations_directory->GetMethodAnnotations()->size();
+    directory_buffer[3] = annotations_directory->GetParameterAnnotations() == nullptr ? 0 :
+        annotations_directory->GetParameterAnnotations()->size();
+    uint32_t offset = annotations_directory->GetOffset();
+    offset += Write(directory_buffer, 4 * sizeof(uint32_t), offset);
+    if (annotations_directory->GetFieldAnnotations() != nullptr) {
+      for (std::unique_ptr<dex_ir::FieldAnnotation>& field :
+          *annotations_directory->GetFieldAnnotations()) {
+        annotation_buffer[0] = field->GetFieldId()->GetIndex();
+        annotation_buffer[1] = field->GetAnnotationSetItem()->GetOffset();
+        offset += Write(annotation_buffer, 2 * sizeof(uint32_t), offset);
+      }
+    }
+    if (annotations_directory->GetMethodAnnotations() != nullptr) {
+      for (std::unique_ptr<dex_ir::MethodAnnotation>& method :
+          *annotations_directory->GetMethodAnnotations()) {
+        annotation_buffer[0] = method->GetMethodId()->GetIndex();
+        annotation_buffer[1] = method->GetAnnotationSetItem()->GetOffset();
+        offset += Write(annotation_buffer, 2 * sizeof(uint32_t), offset);
+      }
+    }
+    if (annotations_directory->GetParameterAnnotations() != nullptr) {
+      for (std::unique_ptr<dex_ir::ParameterAnnotation>& parameter :
+          *annotations_directory->GetParameterAnnotations()) {
+        annotation_buffer[0] = parameter->GetMethodId()->GetIndex();
+        annotation_buffer[1] = parameter->GetAnnotations()->GetOffset();
+        offset += Write(annotation_buffer, 2 * sizeof(uint32_t), offset);
+      }
+    }
+  }
+}
+
+void DexWriter::WriteDebugInfoItems() {
+  for (std::unique_ptr<dex_ir::DebugInfoItem>& info : header_.GetCollections().DebugInfoItems()) {
+    Write(info->GetDebugInfo(), info->GetDebugInfoSize(), info->GetOffset());
+  }
+}
+
+void DexWriter::WriteCodeItems() {
+  uint16_t uint16_buffer[4];
+  uint32_t uint32_buffer[2];
+  for (std::unique_ptr<dex_ir::CodeItem>& code_item : header_.GetCollections().CodeItems()) {
+    uint16_buffer[0] = code_item->RegistersSize();
+    uint16_buffer[1] = code_item->InsSize();
+    uint16_buffer[2] = code_item->OutsSize();
+    uint16_buffer[3] = code_item->TriesSize();
+    uint32_buffer[0] = code_item->DebugInfo() == nullptr ? 0 : code_item->DebugInfo()->GetOffset();
+    uint32_buffer[1] = code_item->InsnsSize();
+    size_t offset = code_item->GetOffset();
+    offset += Write(uint16_buffer, 4 * sizeof(uint16_t), offset);
+    offset += Write(uint32_buffer, 2 * sizeof(uint32_t), offset);
+    offset += Write(code_item->Insns(), code_item->InsnsSize() * sizeof(uint16_t), offset);
+    if (code_item->TriesSize() != 0) {
+      if (code_item->InsnsSize() % 2 != 0) {
+        uint16_t padding[1] = { 0 };
+        offset += Write(padding, sizeof(uint16_t), offset);
+      }
+      uint32_t start_addr[1];
+      uint16_t insn_count_and_handler_off[2];
+      for (std::unique_ptr<const dex_ir::TryItem>& try_item : *code_item->Tries()) {
+        start_addr[0] = try_item->StartAddr();
+        insn_count_and_handler_off[0] = try_item->InsnCount();
+        insn_count_and_handler_off[1] = try_item->GetHandlers()->GetListOffset();
+        offset += Write(start_addr, sizeof(uint32_t), offset);
+        offset += Write(insn_count_and_handler_off, 2 * sizeof(uint16_t), offset);
+      }
+      // Leave offset pointing to the end of the try items.
+      WriteUleb128(code_item->Handlers()->size(), offset);
+      for (std::unique_ptr<const dex_ir::CatchHandler>& handlers : *code_item->Handlers()) {
+        size_t list_offset = offset + handlers->GetListOffset();
+        uint32_t size = handlers->HasCatchAll() ? (handlers->GetHandlers()->size() - 1) * -1 :
+            handlers->GetHandlers()->size();
+        list_offset += WriteSleb128(size, list_offset);
+        for (std::unique_ptr<const dex_ir::TypeAddrPair>& handler : *handlers->GetHandlers()) {
+          if (handler->GetTypeId() != nullptr) {
+            list_offset += WriteUleb128(handler->GetTypeId()->GetIndex(), list_offset);
+          }
+          list_offset += WriteUleb128(handler->GetAddress(), list_offset);
+        }
+      }
+    }
+  }
+}
+
+void DexWriter::WriteClasses() {
+  uint32_t class_def_buffer[8];
+  for (std::unique_ptr<dex_ir::ClassDef>& class_def : header_.GetCollections().ClassDefs()) {
+    class_def_buffer[0] = class_def->ClassType()->GetIndex();
+    class_def_buffer[1] = class_def->GetAccessFlags();
+    class_def_buffer[2] = class_def->Superclass() == nullptr ? DexFile::kDexNoIndex :
+        class_def->Superclass()->GetIndex();
+    class_def_buffer[3] = class_def->InterfacesOffset();
+    class_def_buffer[4] = class_def->SourceFile() == nullptr ? DexFile::kDexNoIndex :
+        class_def->SourceFile()->GetIndex();
+    class_def_buffer[5] = class_def->Annotations() == nullptr ? 0 :
+        class_def->Annotations()->GetOffset();
+    class_def_buffer[6] = class_def->GetClassData() == nullptr ? 0 :
+        class_def->GetClassData()->GetOffset();
+    class_def_buffer[7] = class_def->StaticValues() == nullptr ? 0 :
+        class_def->StaticValues()->GetOffset();
+    size_t offset = class_def->GetOffset();
+    Write(class_def_buffer, class_def->GetSize(), offset);
+  }
+
+  for (std::unique_ptr<dex_ir::ClassData>& class_data : header_.GetCollections().ClassDatas()) {
+    size_t offset = class_data->GetOffset();
+    offset += WriteUleb128(class_data->StaticFields()->size(), offset);
+    offset += WriteUleb128(class_data->InstanceFields()->size(), offset);
+    offset += WriteUleb128(class_data->DirectMethods()->size(), offset);
+    offset += WriteUleb128(class_data->VirtualMethods()->size(), offset);
+    offset += WriteEncodedFields(class_data->StaticFields(), offset);
+    offset += WriteEncodedFields(class_data->InstanceFields(), offset);
+    offset += WriteEncodedMethods(class_data->DirectMethods(), offset);
+    offset += WriteEncodedMethods(class_data->VirtualMethods(), offset);
+  }
+}
+
+struct MapItemContainer {
+  MapItemContainer(uint32_t type, uint32_t size, uint32_t offset)
+      : type_(type), size_(size), offset_(offset) { }
+
+  bool operator<(const MapItemContainer& other) const {
+    return offset_ > other.offset_;
+  }
+
+  uint32_t type_;
+  uint32_t size_;
+  uint32_t offset_;
+};
+
+void DexWriter::WriteMapItem() {
+  dex_ir::Collections& collection = header_.GetCollections();
+  std::priority_queue<MapItemContainer> queue;
+
+  // Header and index section.
+  queue.push(MapItemContainer(DexFile::kDexTypeHeaderItem, 1, 0));
+  if (collection.StringIdsSize() != 0) {
+    queue.push(MapItemContainer(DexFile::kDexTypeStringIdItem, collection.StringIdsSize(),
+        collection.StringIdsOffset()));
+  }
+  if (collection.TypeIdsSize() != 0) {
+    queue.push(MapItemContainer(DexFile::kDexTypeTypeIdItem, collection.TypeIdsSize(),
+        collection.TypeIdsOffset()));
+  }
+  if (collection.ProtoIdsSize() != 0) {
+    queue.push(MapItemContainer(DexFile::kDexTypeProtoIdItem, collection.ProtoIdsSize(),
+        collection.ProtoIdsOffset()));
+  }
+  if (collection.FieldIdsSize() != 0) {
+    queue.push(MapItemContainer(DexFile::kDexTypeFieldIdItem, collection.FieldIdsSize(),
+        collection.FieldIdsOffset()));
+  }
+  if (collection.MethodIdsSize() != 0) {
+    queue.push(MapItemContainer(DexFile::kDexTypeMethodIdItem, collection.MethodIdsSize(),
+        collection.MethodIdsOffset()));
+  }
+  if (collection.ClassDefsSize() != 0) {
+    queue.push(MapItemContainer(DexFile::kDexTypeClassDefItem, collection.ClassDefsSize(),
+        collection.ClassDefsOffset()));
+  }
+
+  // Data section.
+  queue.push(MapItemContainer(DexFile::kDexTypeMapList, 1, collection.MapItemOffset()));
+  if (collection.TypeListsSize() != 0) {
+    queue.push(MapItemContainer(DexFile::kDexTypeTypeList, collection.TypeListsSize(),
+        collection.TypeListsOffset()));
+  }
+  if (collection.AnnotationSetRefListsSize() != 0) {
+    queue.push(MapItemContainer(DexFile::kDexTypeAnnotationSetRefList,
+        collection.AnnotationSetRefListsSize(), collection.AnnotationSetRefListsOffset()));
+  }
+  if (collection.AnnotationSetItemsSize() != 0) {
+    queue.push(MapItemContainer(DexFile::kDexTypeAnnotationSetItem,
+        collection.AnnotationSetItemsSize(), collection.AnnotationSetItemsOffset()));
+  }
+  if (collection.ClassDatasSize() != 0) {
+    queue.push(MapItemContainer(DexFile::kDexTypeClassDataItem, collection.ClassDatasSize(),
+        collection.ClassDatasOffset()));
+  }
+  if (collection.CodeItemsSize() != 0) {
+    queue.push(MapItemContainer(DexFile::kDexTypeCodeItem, collection.CodeItemsSize(),
+        collection.CodeItemsOffset()));
+  }
+  if (collection.StringDatasSize() != 0) {
+    queue.push(MapItemContainer(DexFile::kDexTypeStringDataItem, collection.StringDatasSize(),
+        collection.StringDatasOffset()));
+  }
+  if (collection.DebugInfoItemsSize() != 0) {
+    queue.push(MapItemContainer(DexFile::kDexTypeDebugInfoItem, collection.DebugInfoItemsSize(),
+        collection.DebugInfoItemsOffset()));
+  }
+  if (collection.AnnotationItemsSize() != 0) {
+    queue.push(MapItemContainer(DexFile::kDexTypeAnnotationItem, collection.AnnotationItemsSize(),
+        collection.AnnotationItemsOffset()));
+  }
+  if (collection.EncodedArrayItemsSize() != 0) {
+    queue.push(MapItemContainer(DexFile::kDexTypeEncodedArrayItem,
+        collection.EncodedArrayItemsSize(), collection.EncodedArrayItemsOffset()));
+  }
+  if (collection.AnnotationsDirectoryItemsSize() != 0) {
+    queue.push(MapItemContainer(DexFile::kDexTypeAnnotationsDirectoryItem,
+        collection.AnnotationsDirectoryItemsSize(), collection.AnnotationsDirectoryItemsOffset()));
+  }
+
+  uint32_t offset = collection.MapItemOffset();
+  uint16_t uint16_buffer[2];
+  uint32_t uint32_buffer[2];
+  uint16_buffer[1] = 0;
+  uint32_buffer[0] = queue.size();
+  offset += Write(uint32_buffer, sizeof(uint32_t), offset);
+  while (!queue.empty()) {
+    const MapItemContainer& map_item = queue.top();
+    uint16_buffer[0] = map_item.type_;
+    uint32_buffer[0] = map_item.size_;
+    uint32_buffer[1] = map_item.offset_;
+    offset += Write(uint16_buffer, 2 * sizeof(uint16_t), offset);
+    offset += Write(uint32_buffer, 2 * sizeof(uint32_t), offset);
+    queue.pop();
+  }
+}
+
+void DexWriter::WriteHeader() {
+  uint32_t buffer[20];
+  dex_ir::Collections& collections = header_.GetCollections();
+  size_t offset = 0;
+  offset += Write(header_.Magic(), 8 * sizeof(uint8_t), offset);
+  buffer[0] = header_.Checksum();
+  offset += Write(buffer, sizeof(uint32_t), offset);
+  offset += Write(header_.Signature(), 20 * sizeof(uint8_t), offset);
+  uint32_t file_size = header_.FileSize();
+  buffer[0] = file_size;
+  buffer[1] = header_.GetSize();
+  buffer[2] = header_.EndianTag();
+  buffer[3] = header_.LinkSize();
+  buffer[4] = header_.LinkOffset();
+  buffer[5] = collections.MapItemOffset();
+  buffer[6] = collections.StringIdsSize();
+  buffer[7] = collections.StringIdsOffset();
+  buffer[8] = collections.TypeIdsSize();
+  buffer[9] = collections.TypeIdsOffset();
+  buffer[10] = collections.ProtoIdsSize();
+  buffer[11] = collections.ProtoIdsOffset();
+  buffer[12] = collections.FieldIdsSize();
+  buffer[13] = collections.FieldIdsOffset();
+  buffer[14] = collections.MethodIdsSize();
+  buffer[15] = collections.MethodIdsOffset();
+  uint32_t class_defs_size = collections.ClassDefsSize();
+  uint32_t class_defs_off = collections.ClassDefsOffset();
+  buffer[16] = class_defs_size;
+  buffer[17] = class_defs_off;
+  uint32_t data_off = class_defs_off + class_defs_size * dex_ir::ClassDef::ItemSize();
+  uint32_t data_size = file_size - data_off;
+  buffer[18] = data_size;
+  buffer[19] = data_off;
+  Write(buffer, 20 * sizeof(uint32_t), offset);
+}
+
+void DexWriter::WriteFile() {
+  if (dex_file_.get() == nullptr) {
+    fprintf(stderr, "Can't open output dex file\n");
+    return;
+  }
+
+  WriteStrings();
+  WriteTypes();
+  WriteTypeLists();
+  WriteProtos();
+  WriteFields();
+  WriteMethods();
+  WriteEncodedArrays();
+  WriteAnnotations();
+  WriteAnnotationSets();
+  WriteAnnotationSetRefs();
+  WriteAnnotationsDirectories();
+  WriteDebugInfoItems();
+  WriteCodeItems();
+  WriteClasses();
+  WriteMapItem();
+  WriteHeader();
+}
+
+void DexWriter::OutputDexFile(dex_ir::Header& header, const char* file_name) {
+  (new DexWriter(header, file_name))->WriteFile();
+}
+
+}  // namespace art
diff --git a/dexlayout/dex_writer.h b/dexlayout/dex_writer.h
new file mode 100644
index 0000000..9104295
--- /dev/null
+++ b/dexlayout/dex_writer.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Header file of an in-memory representation of DEX files.
+ */
+
+#ifndef ART_DEXLAYOUT_DEX_WRITER_H_
+#define ART_DEXLAYOUT_DEX_WRITER_H_
+
+#include "base/unix_file/fd_file.h"
+#include "dex_ir.h"
+#include "os.h"
+
+namespace art {
+
+class DexWriter {
+ public:
+  DexWriter(dex_ir::Header& header, const char* file_name) : header_(header),
+      dex_file_(OS::CreateEmptyFileWriteOnly(file_name)) { }
+
+  static void OutputDexFile(dex_ir::Header& header, const char* file_name);
+
+ private:
+  void WriteFile();
+
+  size_t Write(const void* buffer, size_t length, size_t offset);
+  size_t WriteSleb128(uint32_t value, size_t offset);
+  size_t WriteUleb128(uint32_t value, size_t offset);
+  size_t WriteEncodedValue(dex_ir::EncodedValue* encoded_value, size_t offset);
+  size_t WriteEncodedValueHeader(int8_t value_type, size_t value_arg, size_t offset);
+  size_t WriteEncodedArray(dex_ir::EncodedValueVector* values, size_t offset);
+  size_t WriteEncodedAnnotation(dex_ir::EncodedAnnotation* annotation, size_t offset);
+  size_t WriteEncodedFields(dex_ir::FieldItemVector* fields, size_t offset);
+  size_t WriteEncodedMethods(dex_ir::MethodItemVector* methods, size_t offset);
+
+  void WriteStrings();
+  void WriteTypes();
+  void WriteTypeLists();
+  void WriteProtos();
+  void WriteFields();
+  void WriteMethods();
+  void WriteEncodedArrays();
+  void WriteAnnotations();
+  void WriteAnnotationSets();
+  void WriteAnnotationSetRefs();
+  void WriteAnnotationsDirectories();
+  void WriteDebugInfoItems();
+  void WriteCodeItems();
+  void WriteClasses();
+  void WriteMapItem();
+  void WriteHeader();
+
+  dex_ir::Header& header_;
+  std::unique_ptr<File> dex_file_;
+
+  DISALLOW_COPY_AND_ASSIGN(DexWriter);
+};
+
+
+}  // namespace art
+
+#endif  // ART_DEXLAYOUT_DEX_WRITER_H_
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index e614137..a9ae55f 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -30,11 +30,11 @@
 #include <sstream>
 #include <vector>
 
-#include "base/unix_file/fd_file.h"
 #include "dex_ir_builder.h"
 #include "dex_file-inl.h"
 #include "dex_instruction-inl.h"
 #include "dex_visualize.h"
+#include "dex_writer.h"
 #include "jit/offline_profiling_info.h"
 #include "os.h"
 #include "utils.h"
@@ -251,10 +251,12 @@
     return "<no signature>";
   }
 
-  const std::vector<const dex_ir::TypeId*>& params = proto->Parameters();
   std::string result("(");
-  for (uint32_t i = 0; i < params.size(); ++i) {
-    result += params[i]->GetStringId()->Data();
+  const dex_ir::TypeList* type_list = proto->Parameters();
+  if (type_list != nullptr) {
+    for (const dex_ir::TypeId* type_id : *type_list->GetTypeList()) {
+      result += type_id->GetStringId()->Data();
+    }
   }
   result += ")";
   result += proto->ReturnType()->GetStringId()->Data();
@@ -673,7 +675,7 @@
     const uint32_t start = try_item->StartAddr();
     const uint32_t end = start + try_item->InsnCount();
     fprintf(out_file_, "        0x%04x - 0x%04x\n", start, end);
-    for (auto& handler : try_item->GetHandlers()) {
+    for (auto& handler : *try_item->GetHandlers()->GetHandlers()) {
       const dex_ir::TypeId* type_id = handler->GetTypeId();
       const char* descriptor = (type_id == nullptr) ? "<any>" : type_id->GetStringId()->Data();
       fprintf(out_file_, "          %s -> 0x%04x\n", descriptor, handler->GetAddress());
@@ -1502,96 +1504,6 @@
 }
 
 /*
-static uint32_t GetDataSectionOffset(dex_ir::Header& header) {
-  return dex_ir::Header::ItemSize() +
-      header.GetCollections().StringIdsSize() * dex_ir::StringId::ItemSize() +
-      header.GetCollections().TypeIdsSize() * dex_ir::TypeId::ItemSize() +
-      header.GetCollections().ProtoIdsSize() * dex_ir::ProtoId::ItemSize() +
-      header.GetCollections().FieldIdsSize() * dex_ir::FieldId::ItemSize() +
-      header.GetCollections().MethodIdsSize() * dex_ir::MethodId::ItemSize() +
-      header.GetCollections().ClassDefsSize() * dex_ir::ClassDef::ItemSize();
-}
-
-static bool Align(File* file, uint32_t& offset) {
-  uint8_t zero_buffer[] = { 0, 0, 0 };
-  uint32_t zeroes = (-offset) & 3;
-  if (zeroes > 0) {
-    if (!file->PwriteFully(zero_buffer, zeroes, offset)) {
-      return false;
-    }
-    offset += zeroes;
-  }
-  return true;
-}
-
-static bool WriteStrings(File* dex_file, dex_ir::Header& header,
-                         uint32_t& index_offset, uint32_t& data_offset) {
-  uint32_t index = 0;
-  uint32_t index_buffer[1];
-  uint32_t string_length;
-  uint32_t length_length;
-  uint8_t length_buffer[8];
-  for (std::unique_ptr<dex_ir::StringId>& string_id : header.GetCollections().StringIds()) {
-    string_id->SetOffset(index);
-    index_buffer[0] = data_offset;
-    string_length = strlen(string_id->Data());
-    length_length = UnsignedLeb128Size(string_length);
-    EncodeUnsignedLeb128(length_buffer, string_length);
-
-    if (!dex_file->PwriteFully(index_buffer, 4, index_offset) ||
-        !dex_file->PwriteFully(length_buffer, length_length, data_offset) ||
-        !dex_file->PwriteFully(string_id->Data(), string_length, data_offset + length_length)) {
-      return false;
-    }
-
-    index++;
-    index_offset += 4;
-    data_offset += string_length + length_length;
-  }
-  return true;
-}
-
-static bool WriteTypes(File* dex_file, dex_ir::Header& header, uint32_t& index_offset) {
-  uint32_t index = 0;
-  uint32_t index_buffer[1];
-  for (std::unique_ptr<dex_ir::TypeId>& type_id : header.GetCollections().TypeIds()) {
-    type_id->SetIndex(index);
-    index_buffer[0] = type_id->GetStringId()->GetOffset();
-
-    if (!dex_file->PwriteFully(index_buffer, 4, index_offset)) {
-      return false;
-    }
-
-    index++;
-    index_offset += 4;
-  }
-  return true;
-}
-
-static bool WriteTypeLists(File* dex_file, dex_ir::Header& header, uint32_t& data_offset) {
-  if (!Align(dex_file, data_offset)) {
-    return false;
-  }
-
-  return true;
-}
-
-static void OutputDexFile(dex_ir::Header& header, const char* file_name) {
-  LOG(INFO) << "FILE NAME: " << file_name;
-  std::unique_ptr<File> dex_file(OS::CreateEmptyFileWriteOnly(file_name));
-  if (dex_file == nullptr) {
-    fprintf(stderr, "Can't open %s\n", file_name);
-    return;
-  }
-
-  uint32_t index_offset = dex_ir::Header::ItemSize();
-  uint32_t data_offset = GetDataSectionOffset(header);
-  WriteStrings(dex_file.get(), header, index_offset, data_offset);
-  WriteTypes(dex_file.get(), header, index_offset);
-}
-*/
-
-/*
  * Dumps the requested sections of the file.
  */
 static void ProcessDexFile(const char* file_name, const DexFile* dex_file, size_t dex_file_index) {
@@ -1634,13 +1546,13 @@
     fprintf(out_file_, "</api>\n");
   }
 
-  /*
   // Output dex file.
-  if (options_.output_dex_files_) {
-    std::string output_dex_filename = dex_file->GetLocation() + ".out";
-    OutputDexFile(*header, output_dex_filename.c_str());
+  if (options_.output_dex_directory_ != nullptr) {
+    std::string output_location(options_.output_dex_directory_);
+    size_t last_slash = dex_file->GetLocation().rfind("/");
+    output_location.append(dex_file->GetLocation().substr(last_slash));
+    DexWriter::OutputDexFile(*header, output_location.c_str());
   }
-  */
 }
 
 /*
diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h
index c4892d2..c01eb79 100644
--- a/dexlayout/dexlayout.h
+++ b/dexlayout/dexlayout.h
@@ -43,7 +43,6 @@
   bool disassemble_;
   bool exports_only_;
   bool ignore_bad_checksum_;
-  bool output_dex_files_;
   bool show_annotations_;
   bool show_cfg_;
   bool show_file_headers_;
@@ -51,6 +50,7 @@
   bool verbose_;
   bool visualize_pattern_;
   OutputFormat output_format_;
+  const char* output_dex_directory_;
   const char* output_file_name_;
   const char* profile_file_name_;
 };
diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc
index f385b09..728e389 100644
--- a/dexlayout/dexlayout_main.cc
+++ b/dexlayout/dexlayout_main.cc
@@ -43,7 +43,7 @@
 static void Usage(void) {
   fprintf(stderr, "Copyright (C) 2016 The Android Open Source Project\n\n");
   fprintf(stderr, "%s: [-a] [-c] [-d] [-e] [-f] [-h] [-i] [-l layout] [-o outfile] [-p profile]"
-                  " [-s] [-w] dexfile...\n\n", kProgramName);
+                  " [-s] [-w directory] dexfile...\n\n", kProgramName);
   fprintf(stderr, " -a : display annotations\n");
   fprintf(stderr, " -b : build dex_ir\n");
   fprintf(stderr, " -c : verify checksum and exit\n");
@@ -57,7 +57,7 @@
   fprintf(stderr, " -o : output file name (defaults to stdout)\n");
   fprintf(stderr, " -p : profile file name (defaults to no profile)\n");
   fprintf(stderr, " -s : visualize reference pattern\n");
-  fprintf(stderr, " -w : output dex files\n");
+  fprintf(stderr, " -w : output dex directory \n");
 }
 
 /*
@@ -75,7 +75,7 @@
 
   // Parse all arguments.
   while (1) {
-    const int ic = getopt(argc, argv, "abcdefghil:o:p:sw");
+    const int ic = getopt(argc, argv, "abcdefghil:o:p:sw:");
     if (ic < 0) {
       break;  // done
     }
@@ -127,8 +127,8 @@
         options_.visualize_pattern_ = true;
         options_.verbose_ = false;
         break;
-      case 'w':  // output dex files
-        options_.output_dex_files_ = true;
+      case 'w':  // output dex files directory
+        options_.output_dex_directory_ = optarg;
         break;
       default:
         want_usage = true;
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index 42b64c3..89544d7 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -31,43 +31,83 @@
  protected:
   virtual void SetUp() {
     CommonRuntimeTest::SetUp();
-    // TODO: Test with other dex files for improved coverage.
-    // Dogfood our own lib core dex file.
-    dex_file_ = GetLibCoreDexFileNames()[0];
   }
 
-  // Runs test with given arguments.
-  bool Exec(std::string* error_msg) {
+  // Runs FullPlainOutput test.
+  bool FullPlainOutputExec(std::string* error_msg) {
     // TODO: dexdump2 -> dexdump ?
     ScratchFile dexdump_output;
     std::string dexdump_filename = dexdump_output.GetFilename();
     std::string dexdump = GetTestAndroidRoot() + "/bin/dexdump2";
     EXPECT_TRUE(OS::FileExists(dexdump.c_str())) << dexdump << " should be a valid file path";
-    std::vector<std::string> dexdump_exec_argv =
-        { dexdump, "-d", "-f", "-h", "-l", "plain", "-o", dexdump_filename, dex_file_ };
 
     ScratchFile dexlayout_output;
     std::string dexlayout_filename = dexlayout_output.GetFilename();
     std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout";
     EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path";
-    std::vector<std::string> dexlayout_exec_argv =
-        { dexlayout, "-d", "-f", "-h", "-l", "plain", "-o", dexlayout_filename, dex_file_ };
 
-    if (!::art::Exec(dexdump_exec_argv, error_msg)) {
-      return false;
-    }
-    if (!::art::Exec(dexlayout_exec_argv, error_msg)) {
-      return false;
-    }
-    std::vector<std::string> diff_exec_argv =
-        { "/usr/bin/diff", dexdump_filename, dexlayout_filename };
-    if (!::art::Exec(diff_exec_argv, error_msg)) {
-      return false;
+    for (const std::string &dex_file : GetLibCoreDexFileNames()) {
+      std::vector<std::string> dexdump_exec_argv =
+          { dexdump, "-d", "-f", "-h", "-l", "plain", "-o", dexdump_filename, dex_file };
+      std::vector<std::string> dexlayout_exec_argv =
+          { dexlayout, "-d", "-f", "-h", "-l", "plain", "-o", dexlayout_filename, dex_file };
+
+      if (!::art::Exec(dexdump_exec_argv, error_msg)) {
+        return false;
+      }
+      if (!::art::Exec(dexlayout_exec_argv, error_msg)) {
+        return false;
+      }
+      std::vector<std::string> diff_exec_argv =
+          { "/usr/bin/diff", dexdump_filename, dexlayout_filename };
+      if (!::art::Exec(diff_exec_argv, error_msg)) {
+        return false;
+      }
     }
     return true;
   }
 
-  std::string dex_file_;
+  // Runs DexFileOutput test.
+  bool DexFileOutputExec(std::string* error_msg) {
+    ScratchFile tmp_file;
+    std::string tmp_name = tmp_file.GetFilename();
+    size_t tmp_last_slash = tmp_name.rfind("/");
+    std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1);
+    std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout";
+    EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path";
+
+    for (const std::string &dex_file : GetLibCoreDexFileNames()) {
+      std::vector<std::string> dexlayout_exec_argv =
+          { dexlayout, "-d", "-f", "-h", "-l", "plain", "-w", tmp_dir, "-o", tmp_name, dex_file };
+
+      if (!::art::Exec(dexlayout_exec_argv, error_msg)) {
+        return false;
+      }
+
+      size_t dex_file_last_slash = dex_file.rfind("/");
+      std::string dex_file_name = dex_file.substr(dex_file_last_slash + 1);
+      std::vector<std::string> unzip_exec_argv =
+          { "/usr/bin/unzip", dex_file, "classes.dex", "-d", tmp_dir};
+      if (!::art::Exec(unzip_exec_argv, error_msg)) {
+        return false;
+      }
+      std::vector<std::string> diff_exec_argv =
+          { "/usr/bin/diff", tmp_dir + "classes.dex" , tmp_dir + dex_file_name };
+      if (!::art::Exec(diff_exec_argv, error_msg)) {
+        return false;
+      }
+      std::vector<std::string> rm_zip_exec_argv = { "/bin/rm", tmp_dir + "classes.dex" };
+      if (!::art::Exec(rm_zip_exec_argv, error_msg)) {
+        return false;
+      }
+      std::vector<std::string> rm_out_exec_argv = { "/bin/rm", tmp_dir + dex_file_name };
+      if (!::art::Exec(rm_out_exec_argv, error_msg)) {
+        return false;
+      }
+    }
+
+    return true;
+  }
 };
 
 
@@ -75,7 +115,14 @@
   // Disable test on target.
   TEST_DISABLED_FOR_TARGET();
   std::string error_msg;
-  ASSERT_TRUE(Exec(&error_msg)) << error_msg;
+  ASSERT_TRUE(FullPlainOutputExec(&error_msg)) << error_msg;
+}
+
+TEST_F(DexLayoutTest, DexFileOutput) {
+  // Disable test on target.
+  TEST_DISABLED_FOR_TARGET();
+  std::string error_msg;
+  ASSERT_TRUE(DexFileOutputExec(&error_msg)) << error_msg;
 }
 
 }  // namespace art
diff --git a/runtime/base/dumpable-inl.h b/runtime/base/dumpable-inl.h
new file mode 100644
index 0000000..2cdf083
--- /dev/null
+++ b/runtime/base/dumpable-inl.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_BASE_DUMPABLE_INL_H_
+#define ART_RUNTIME_BASE_DUMPABLE_INL_H_
+
+#include "base/dumpable.h"
+#include "base/mutex.h"
+#include "thread-inl.h"
+
+namespace art {
+
+template<typename T>
+inline std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable<T>& rhs) {
+  Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
+  rhs.Dump(os);
+  return os;
+}
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_BASE_DUMPABLE_INL_H_
diff --git a/runtime/base/dumpable.h b/runtime/base/dumpable.h
index 9bc4089..9ef8d69 100644
--- a/runtime/base/dumpable.h
+++ b/runtime/base/dumpable.h
@@ -20,6 +20,7 @@
 #include <ostream>
 
 #include "base/macros.h"
+#include "base/mutex.h"
 
 namespace art {
 
@@ -50,6 +51,27 @@
   return os;
 }
 
+template<typename T>
+class MutatorLockedDumpable {
+ public:
+  explicit MutatorLockedDumpable(T& value) REQUIRES_SHARED(Locks::mutator_lock_) : value_(value) {}
+
+  void Dump(std::ostream& os) const REQUIRES_SHARED(Locks::mutator_lock_) {
+    value_.Dump(os);
+  }
+
+ private:
+  const T& value_;
+
+  DISALLOW_COPY_AND_ASSIGN(MutatorLockedDumpable);
+};
+
+template<typename T>
+std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable<T>& rhs)
+  // TODO: should be REQUIRES_SHARED(Locks::mutator_lock_) however annotalysis
+  //       currently fails for this.
+    NO_THREAD_SAFETY_ANALYSIS;
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_BASE_DUMPABLE_H_
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 53135b6..ed0f0c0 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4297,11 +4297,11 @@
   CHECK_EQ(interfaces_sfield.GetDeclaringClass(), klass.Get());
   interfaces_sfield.SetObject<false>(
       klass.Get(),
-      soa.Decode<mirror::ObjectArray<mirror::Class>>(interfaces).Ptr());
+      soa.Decode<mirror::ObjectArray<mirror::Class>>(interfaces));
   CHECK_EQ(throws_sfield.GetDeclaringClass(), klass.Get());
   throws_sfield.SetObject<false>(
       klass.Get(),
-      soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>>(throws).Ptr());
+      soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>>(throws));
 
   {
     // Lock on klass is released. Lock new class object.
@@ -4331,9 +4331,9 @@
     CHECK_EQ(PrettyField(klass->GetStaticField(1)), throws_field_name);
 
     CHECK_EQ(klass.Get()->GetInterfaces(),
-             soa.Decode<mirror::ObjectArray<mirror::Class>>(interfaces).Ptr());
+             soa.Decode<mirror::ObjectArray<mirror::Class>>(interfaces));
     CHECK_EQ(klass.Get()->GetThrows(),
-             soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>>(throws).Ptr());
+             soa.Decode<mirror::ObjectArray<mirror::ObjectArray<mirror::Class>>>(throws));
   }
   return klass.Get();
 }
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 5466539..d45495c 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -875,7 +875,7 @@
   uint32_t type_idx = klass->GetClassDef()->class_idx_;
   ObjPtr<mirror::DexCache> dex_cache = klass->GetDexCache();
   const DexFile& dex_file = klass->GetDexFile();
-  EXPECT_EQ(dex_cache->GetResolvedType(type_idx), klass.Ptr());
+  EXPECT_OBJ_PTR_EQ(dex_cache->GetResolvedType(type_idx), klass);
   EXPECT_OBJ_PTR_EQ(
       class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache, class_loader.Get()),
       klass);
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index a7feeef..7006f70 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -1995,7 +1995,7 @@
   CHECK(thread_object != nullptr) << error;
   ArtField* java_lang_Thread_name_field =
       soa.DecodeField(WellKnownClasses::java_lang_Thread_name);
-  ObjPtr<mirror::String> s(java_lang_Thread_name_field->GetObject(thread_object));
+  ObjPtr<mirror::String> s(java_lang_Thread_name_field->GetObject(thread_object)->AsString());
   if (s != nullptr) {
     *name = s->ToModifiedUtf8();
   }
diff --git a/runtime/indirect_reference_table-inl.h b/runtime/indirect_reference_table-inl.h
index 5cc1de2..e05f8f3 100644
--- a/runtime/indirect_reference_table-inl.h
+++ b/runtime/indirect_reference_table-inl.h
@@ -19,7 +19,9 @@
 
 #include "indirect_reference_table.h"
 
+#include "base/dumpable.h"
 #include "gc_root-inl.h"
+#include "obj_ptr-inl.h"
 #include "runtime-inl.h"
 #include "verify_object-inl.h"
 
@@ -82,17 +84,17 @@
 }
 
 template<ReadBarrierOption kReadBarrierOption>
-inline mirror::Object* IndirectReferenceTable::Get(IndirectRef iref) const {
+inline ObjPtr<mirror::Object> IndirectReferenceTable::Get(IndirectRef iref) const {
   if (!GetChecked(iref)) {
     return nullptr;
   }
   uint32_t idx = ExtractIndex(iref);
-  mirror::Object* obj = table_[idx].GetReference()->Read<kReadBarrierOption>();
-  VerifyObject(obj);
+  ObjPtr<mirror::Object> obj = table_[idx].GetReference()->Read<kReadBarrierOption>();
+  VerifyObject(obj.Ptr());
   return obj;
 }
 
-inline void IndirectReferenceTable::Update(IndirectRef iref, mirror::Object* obj) {
+inline void IndirectReferenceTable::Update(IndirectRef iref, ObjPtr<mirror::Object> obj) {
   if (!GetChecked(iref)) {
     LOG(WARNING) << "IndirectReferenceTable Update failed to find reference " << iref;
     return;
@@ -101,6 +103,19 @@
   table_[idx].SetReference(obj);
 }
 
+inline void IrtEntry::Add(ObjPtr<mirror::Object> obj) {
+  ++serial_;
+  if (serial_ == kIRTPrevCount) {
+    serial_ = 0;
+  }
+  references_[serial_] = GcRoot<mirror::Object>(obj);
+}
+
+inline void IrtEntry::SetReference(ObjPtr<mirror::Object> obj) {
+  DCHECK_LT(serial_, kIRTPrevCount);
+  references_[serial_] = GcRoot<mirror::Object>(obj);
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_INDIRECT_REFERENCE_TABLE_INL_H_
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index b742ccc..d59bb39 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -16,6 +16,7 @@
 
 #include "indirect_reference_table-inl.h"
 
+#include "base/dumpable-inl.h"
 #include "base/systrace.h"
 #include "jni_internal.h"
 #include "nth_caller_visitor.h"
@@ -46,32 +47,6 @@
   return "IndirectRefKind Error";
 }
 
-template<typename T>
-class MutatorLockedDumpable {
- public:
-  explicit MutatorLockedDumpable(T& value)
-      REQUIRES_SHARED(Locks::mutator_lock_) : value_(value) {
-  }
-
-  void Dump(std::ostream& os) const REQUIRES_SHARED(Locks::mutator_lock_) {
-    value_.Dump(os);
-  }
-
- private:
-  T& value_;
-
-  DISALLOW_COPY_AND_ASSIGN(MutatorLockedDumpable);
-};
-
-template<typename T>
-std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable<T>& rhs)
-// TODO: should be REQUIRES_SHARED(Locks::mutator_lock_) however annotalysis
-//       currently fails for this.
-    NO_THREAD_SAFETY_ANALYSIS {
-  rhs.Dump(os);
-  return os;
-}
-
 void IndirectReferenceTable::AbortIfNoCheckJNI(const std::string& msg) {
   // If -Xcheck:jni is on, it'll give a more detailed error before aborting.
   JavaVMExt* vm = Runtime::Current()->GetJavaVM();
@@ -118,13 +93,13 @@
   return table_mem_map_.get() != nullptr;
 }
 
-IndirectRef IndirectReferenceTable::Add(uint32_t cookie, mirror::Object* obj) {
+IndirectRef IndirectReferenceTable::Add(uint32_t cookie, ObjPtr<mirror::Object> obj) {
   IRTSegmentState prevState;
   prevState.all = cookie;
   size_t topIndex = segment_state_.parts.topIndex;
 
   CHECK(obj != nullptr);
-  VerifyObject(obj);
+  VerifyObject(obj.Ptr());
   DCHECK(table_ != nullptr);
   DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles);
 
@@ -171,9 +146,9 @@
 void IndirectReferenceTable::AssertEmpty() {
   for (size_t i = 0; i < Capacity(); ++i) {
     if (!table_[i].GetReference()->IsNull()) {
-      ScopedObjectAccess soa(Thread::Current());
       LOG(FATAL) << "Internal Error: non-empty local reference table\n"
                  << MutatorLockedDumpable<IndirectReferenceTable>(*this);
+      UNREACHABLE();
     }
   }
 }
@@ -299,7 +274,7 @@
   os << kind_ << " table dump:\n";
   ReferenceTable::Table entries;
   for (size_t i = 0; i < Capacity(); ++i) {
-    mirror::Object* obj = table_[i].GetReference()->Read<kWithoutReadBarrier>();
+    ObjPtr<mirror::Object> obj = table_[i].GetReference()->Read<kWithoutReadBarrier>();
     if (obj != nullptr) {
       obj = table_[i].GetReference()->Read();
       entries.push_back(GcRoot<mirror::Object>(obj));
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index e194f79..64de7a8 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -25,6 +25,7 @@
 #include "base/logging.h"
 #include "base/mutex.h"
 #include "gc_root.h"
+#include "obj_ptr.h"
 #include "object_callbacks.h"
 #include "offsets.h"
 #include "read_barrier_option.h"
@@ -200,24 +201,18 @@
 static const size_t kIRTPrevCount = kIsDebugBuild ? 7 : 3;
 class IrtEntry {
  public:
-  void Add(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
-    ++serial_;
-    if (serial_ == kIRTPrevCount) {
-      serial_ = 0;
-    }
-    references_[serial_] = GcRoot<mirror::Object>(obj);
-  }
+  void Add(ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_);
+
   GcRoot<mirror::Object>* GetReference() {
     DCHECK_LT(serial_, kIRTPrevCount);
     return &references_[serial_];
   }
+
   uint32_t GetSerial() const {
     return serial_;
   }
-  void SetReference(mirror::Object* obj) {
-    DCHECK_LT(serial_, kIRTPrevCount);
-    references_[serial_] = GcRoot<mirror::Object>(obj);
-  }
+
+  void SetReference(ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_);
 
  private:
   uint32_t serial_;
@@ -237,7 +232,7 @@
     return *this;
   }
 
-  GcRoot<mirror::Object>* operator*() {
+  GcRoot<mirror::Object>* operator*() REQUIRES_SHARED(Locks::mutator_lock_) {
     // This does not have a read barrier as this is used to visit roots.
     return table_[i_].GetReference();
   }
@@ -277,7 +272,7 @@
    * Returns nullptr if the table is full (max entries reached, or alloc
    * failed during expansion).
    */
-  IndirectRef Add(uint32_t cookie, mirror::Object* obj)
+  IndirectRef Add(uint32_t cookie, ObjPtr<mirror::Object> obj)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   /*
@@ -286,12 +281,13 @@
    * Returns kInvalidIndirectRefObject if iref is invalid.
    */
   template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
-  mirror::Object* Get(IndirectRef iref) const REQUIRES_SHARED(Locks::mutator_lock_)
+  ObjPtr<mirror::Object> Get(IndirectRef iref) const REQUIRES_SHARED(Locks::mutator_lock_)
       ALWAYS_INLINE;
 
   // Synchronized get which reads a reference, acquiring a lock if necessary.
   template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
-  mirror::Object* SynchronizedGet(IndirectRef iref) const REQUIRES_SHARED(Locks::mutator_lock_) {
+  ObjPtr<mirror::Object> SynchronizedGet(IndirectRef iref) const
+      REQUIRES_SHARED(Locks::mutator_lock_) {
     return Get<kReadBarrierOption>(iref);
   }
 
@@ -300,7 +296,7 @@
    *
    * Updates an existing indirect reference to point to a new object.
    */
-  void Update(IndirectRef iref, mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_);
+  void Update(IndirectRef iref, ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_);
 
   /*
    * Remove an existing entry.
@@ -313,7 +309,7 @@
    */
   bool Remove(uint32_t cookie, IndirectRef iref);
 
-  void AssertEmpty();
+  void AssertEmpty() REQUIRES_SHARED(Locks::mutator_lock_);
 
   void Dump(std::ostream& os) const REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -377,7 +373,7 @@
   static void AbortIfNoCheckJNI(const std::string& msg);
 
   /* extra debugging checks */
-  bool GetChecked(IndirectRef) const;
+  bool GetChecked(IndirectRef) const REQUIRES_SHARED(Locks::mutator_lock_);
   bool CheckEntry(const char*, IndirectRef, int) const;
 
   /* semi-public - read/write by jni down calls */
diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc
index 58d487d..0380f3e 100644
--- a/runtime/indirect_reference_table_test.cc
+++ b/runtime/indirect_reference_table_test.cc
@@ -81,9 +81,9 @@
   EXPECT_TRUE(iref2 != nullptr);
   CheckDump(&irt, 3, 3);
 
-  EXPECT_EQ(obj0, irt.Get(iref0));
-  EXPECT_EQ(obj1, irt.Get(iref1));
-  EXPECT_EQ(obj2, irt.Get(iref2));
+  EXPECT_OBJ_PTR_EQ(obj0, irt.Get(iref0));
+  EXPECT_OBJ_PTR_EQ(obj1, irt.Get(iref1));
+  EXPECT_OBJ_PTR_EQ(obj2, irt.Get(iref2));
 
   EXPECT_TRUE(irt.Remove(cookie, iref0));
   CheckDump(&irt, 2, 2);
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 6b28110..7d54d0a 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -185,6 +185,13 @@
     return false;
   }
   const uint32_t vtable_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
+  // Debug code for b/31357497. To be removed.
+  if (kUseReadBarrier) {
+    CHECK(receiver->GetClass() != nullptr)
+        << "Null class found in object " << receiver << " in region type "
+        << Runtime::Current()->GetHeap()->ConcurrentCopyingCollector()->
+            RegionSpace()->GetRegionType(receiver);
+  }
   CHECK(receiver->GetClass()->ShouldHaveEmbeddedVTable());
   ArtMethod* const called_method = receiver->GetClass()->GetEmbeddedVTableEntry(
       vtable_idx, kRuntimePointerSize);
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 89cbbe6..ac5401f 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -1441,15 +1441,15 @@
   JNIEnvExt* env = self->GetJniEnv();
   ScopedObjectAccessUnchecked soa(self);
 
-  mirror::Object* java_method_obj = shadow_frame->GetVRegReference(arg_offset);
+  ObjPtr<mirror::Object> java_method_obj = shadow_frame->GetVRegReference(arg_offset);
   ScopedLocalRef<jobject> java_method(env,
       java_method_obj == nullptr ? nullptr :env->AddLocalReference<jobject>(java_method_obj));
 
-  mirror::Object* java_receiver_obj = shadow_frame->GetVRegReference(arg_offset + 1);
+  ObjPtr<mirror::Object> java_receiver_obj = shadow_frame->GetVRegReference(arg_offset + 1);
   ScopedLocalRef<jobject> java_receiver(env,
       java_receiver_obj == nullptr ? nullptr : env->AddLocalReference<jobject>(java_receiver_obj));
 
-  mirror::Object* java_args_obj = shadow_frame->GetVRegReference(arg_offset + 2);
+  ObjPtr<mirror::Object> java_args_obj = shadow_frame->GetVRegReference(arg_offset + 2);
   ScopedLocalRef<jobject> java_args(env,
       java_args_obj == nullptr ? nullptr : env->AddLocalReference<jobject>(java_args_obj));
 
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index 215f2b3..101c146 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -538,7 +538,7 @@
     return nullptr;
   }
   WriterMutexLock mu(self, globals_lock_);
-  IndirectRef ref = globals_.Add(IRT_FIRST_SEGMENT, obj.Ptr());
+  IndirectRef ref = globals_.Add(IRT_FIRST_SEGMENT, obj);
   return reinterpret_cast<jobject>(ref);
 }
 
@@ -550,7 +550,7 @@
   while (UNLIKELY(!MayAccessWeakGlobals(self))) {
     weak_globals_add_condition_.WaitHoldingLocks(self);
   }
-  IndirectRef ref = weak_globals_.Add(IRT_FIRST_SEGMENT, obj.Ptr());
+  IndirectRef ref = weak_globals_.Add(IRT_FIRST_SEGMENT, obj);
   return reinterpret_cast<jweak>(ref);
 }
 
@@ -641,7 +641,7 @@
 }
 
 mirror::Object* JavaVMExt::DecodeGlobal(IndirectRef ref) {
-  return globals_.SynchronizedGet(ref);
+  return globals_.SynchronizedGet(ref).Ptr();
 }
 
 void JavaVMExt::UpdateGlobal(Thread* self, IndirectRef ref, mirror::Object* result) {
@@ -669,7 +669,7 @@
   // if MayAccessWeakGlobals is false.
   DCHECK_EQ(GetIndirectRefKind(ref), kWeakGlobal);
   if (LIKELY(MayAccessWeakGlobalsUnlocked(self))) {
-    return weak_globals_.SynchronizedGet(ref);
+    return weak_globals_.SynchronizedGet(ref).Ptr();
   }
   MutexLock mu(self, weak_globals_lock_);
   return DecodeWeakGlobalLocked(self, ref);
@@ -682,7 +682,7 @@
   while (UNLIKELY(!MayAccessWeakGlobals(self))) {
     weak_globals_add_condition_.WaitHoldingLocks(self);
   }
-  return weak_globals_.Get(ref);
+  return weak_globals_.Get(ref).Ptr();
 }
 
 mirror::Object* JavaVMExt::DecodeWeakGlobalDuringShutdown(Thread* self, IndirectRef ref) {
@@ -695,7 +695,7 @@
   if (!kUseReadBarrier) {
     DCHECK(allow_accessing_weak_globals_.LoadSequentiallyConsistent());
   }
-  return weak_globals_.SynchronizedGet(ref);
+  return weak_globals_.SynchronizedGet(ref).Ptr();
 }
 
 bool JavaVMExt::IsWeakGlobalCleared(Thread* self, IndirectRef ref) {
diff --git a/runtime/jni_env_ext-inl.h b/runtime/jni_env_ext-inl.h
index 685b056..2cc7342 100644
--- a/runtime/jni_env_ext-inl.h
+++ b/runtime/jni_env_ext-inl.h
@@ -19,12 +19,14 @@
 
 #include "jni_env_ext.h"
 
+#include "indirect_reference_table-inl.h"
+#include "obj_ptr-inl.h"
 #include "utils.h"
 
 namespace art {
 
 template<typename T>
-inline T JNIEnvExt::AddLocalReference(mirror::Object* obj) {
+inline T JNIEnvExt::AddLocalReference(ObjPtr<mirror::Object> obj) {
   IndirectRef ref = locals.Add(local_ref_cookie, obj);
 
   // TODO: fix this to understand PushLocalFrame, so we can turn it on.
diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h
index 79dfb0d..121f848 100644
--- a/runtime/jni_env_ext.h
+++ b/runtime/jni_env_ext.h
@@ -47,8 +47,7 @@
   void PopFrame() REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<typename T>
-  T AddLocalReference(mirror::Object* obj)
-      REQUIRES_SHARED(Locks::mutator_lock_);
+  T AddLocalReference(ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_);
 
   static Offset SegmentStateOffset(size_t pointer_size);
   static Offset LocalRefCookieOffset(size_t pointer_size);
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 1cfed74..cc088b8 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -448,7 +448,7 @@
 inline bool Class::CheckResolvedFieldAccess(ObjPtr<Class> access_to,
                                             ArtField* field,
                                             uint32_t field_idx) {
-  return ResolvedFieldAccessTest<true, true>(access_to.Ptr(), field, field_idx, nullptr);
+  return ResolvedFieldAccessTest<true, true>(access_to, field, field_idx, nullptr);
 }
 
 inline bool Class::CanAccessResolvedMethod(Class* access_to, ArtMethod* method,
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 87bff5f..2a5c04d 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -677,11 +677,10 @@
     if (caller.Get() == nullptr) {
       caller.Assign(GetCallingClass(soa.Self(), 1));
     }
-    if (UNLIKELY(caller.Get() != nullptr && !VerifyAccess(
-        MakeObjPtr(receiver.Get()),
-        MakeObjPtr(declaring_class),
-        constructor->GetAccessFlags(),
-        MakeObjPtr(caller.Get())))) {
+    if (UNLIKELY(caller.Get() != nullptr && !VerifyAccess(receiver.Get(),
+                                                          declaring_class,
+                                                          constructor->GetAccessFlags(),
+                                                          caller.Get()))) {
       soa.Self()->ThrowNewExceptionF(
           "Ljava/lang/IllegalAccessException;", "%s is not accessible from %s",
           PrettyMethod(constructor).c_str(), PrettyClass(caller.Get()).c_str());
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 07b59dd..90def44 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -31,8 +31,9 @@
 namespace art {
 
 template<bool kIsSet>
-ALWAYS_INLINE inline static bool VerifyFieldAccess(Thread* self, mirror::Field* field,
-                                                   mirror::Object* obj)
+ALWAYS_INLINE inline static bool VerifyFieldAccess(Thread* self,
+                                                   ObjPtr<mirror::Field> field,
+                                                   ObjPtr<mirror::Object> obj)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   if (kIsSet && field->IsFinal()) {
     ThrowIllegalAccessException(
@@ -45,8 +46,8 @@
   }
   ObjPtr<mirror::Class> calling_class;
   if (!VerifyAccess(self,
-                    MakeObjPtr(obj),
-                    MakeObjPtr(field->GetDeclaringClass()),
+                    obj,
+                    field->GetDeclaringClass(),
                     field->GetAccessFlags(),
                     &calling_class,
                     1)) {
@@ -63,8 +64,10 @@
 }
 
 template<bool kAllowReferences>
-ALWAYS_INLINE inline static bool GetFieldValue(mirror::Object* o, mirror::Field* f,
-                                               Primitive::Type field_type, JValue* value)
+ALWAYS_INLINE inline static bool GetFieldValue(ObjPtr<mirror::Object> o,
+                                               ObjPtr<mirror::Field> f,
+                                               Primitive::Type field_type,
+                                               JValue* value)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK_EQ(value->GetJ(), INT64_C(0));
   MemberOffset offset(f->GetOffset());
@@ -108,27 +111,28 @@
 }
 
 ALWAYS_INLINE inline static bool CheckReceiver(const ScopedFastNativeObjectAccess& soa,
-                                               jobject j_rcvr, mirror::Field** f,
-                                               mirror::Object** class_or_rcvr)
+                                               jobject j_rcvr,
+                                               ObjPtr<mirror::Field>* f,
+                                               ObjPtr<mirror::Object>* class_or_rcvr)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   soa.Self()->AssertThreadSuspensionIsAllowable();
-  mirror::Class* declaringClass = (*f)->GetDeclaringClass();
+  ObjPtr<mirror::Class> declaring_class = (*f)->GetDeclaringClass();
   if ((*f)->IsStatic()) {
-    if (UNLIKELY(!declaringClass->IsInitialized())) {
+    if (UNLIKELY(!declaring_class->IsInitialized())) {
       StackHandleScope<2> hs(soa.Self());
-      HandleWrapper<mirror::Field> h_f(hs.NewHandleWrapper(f));
-      HandleWrapper<mirror::Class> h_klass(hs.NewHandleWrapper(&declaringClass));
+      HandleWrapperObjPtr<mirror::Field> h_f(hs.NewHandleWrapper(f));
+      HandleWrapperObjPtr<mirror::Class> h_klass(hs.NewHandleWrapper(&declaring_class));
       ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
       if (UNLIKELY(!class_linker->EnsureInitialized(soa.Self(), h_klass, true, true))) {
         DCHECK(soa.Self()->IsExceptionPending());
         return false;
       }
     }
-    *class_or_rcvr = declaringClass;
+    *class_or_rcvr = declaring_class;
     return true;
   }
-  *class_or_rcvr = soa.Decode<mirror::Object>(j_rcvr).Ptr();
-  if (!VerifyObjectIsClass(MakeObjPtr(*class_or_rcvr), MakeObjPtr(declaringClass))) {
+  *class_or_rcvr = soa.Decode<mirror::Object>(j_rcvr);
+  if (!VerifyObjectIsClass(*class_or_rcvr, declaring_class)) {
     DCHECK(soa.Self()->IsExceptionPending());
     return false;
   }
@@ -137,8 +141,8 @@
 
 static jobject Field_get(JNIEnv* env, jobject javaField, jobject javaObj) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Field* f = soa.Decode<mirror::Field>(javaField).Ptr();
-  mirror::Object* o = nullptr;
+  ObjPtr<mirror::Field> f = soa.Decode<mirror::Field>(javaField);
+  ObjPtr<mirror::Object> o;
   if (!CheckReceiver(soa, javaObj, &f, &o)) {
     DCHECK(soa.Self()->IsExceptionPending());
     return nullptr;
@@ -156,15 +160,16 @@
     DCHECK(soa.Self()->IsExceptionPending());
     return nullptr;
   }
-  return soa.AddLocalReference<jobject>(BoxPrimitive(field_type, value).Ptr());
+  return soa.AddLocalReference<jobject>(BoxPrimitive(field_type, value));
 }
 
 template<Primitive::Type kPrimitiveType>
-ALWAYS_INLINE inline static JValue GetPrimitiveField(JNIEnv* env, jobject javaField,
+ALWAYS_INLINE inline static JValue GetPrimitiveField(JNIEnv* env,
+                                                     jobject javaField,
                                                      jobject javaObj) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Field* f = soa.Decode<mirror::Field>(javaField).Ptr();
-  mirror::Object* o = nullptr;
+  ObjPtr<mirror::Field> f = soa.Decode<mirror::Field>(javaField);
+  ObjPtr<mirror::Object> o;
   if (!CheckReceiver(soa, javaObj, &f, &o)) {
     DCHECK(soa.Self()->IsExceptionPending());
     return JValue();
@@ -234,8 +239,10 @@
   return GetPrimitiveField<Primitive::kPrimShort>(env, javaField, javaObj).GetS();
 }
 
-ALWAYS_INLINE inline static void SetFieldValue(mirror::Object* o, mirror::Field* f,
-                                               Primitive::Type field_type, bool allow_references,
+ALWAYS_INLINE inline static void SetFieldValue(ObjPtr<mirror::Object> o,
+                                               ObjPtr<mirror::Field> f,
+                                               Primitive::Type field_type,
+                                               bool allow_references,
                                                const JValue& new_value)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK(f->GetDeclaringClass()->IsInitialized());
@@ -307,14 +314,14 @@
 
 static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject javaValue) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Field* f = soa.Decode<mirror::Field>(javaField).Ptr();
+  ObjPtr<mirror::Field> f = soa.Decode<mirror::Field>(javaField);
   // Check that the receiver is non-null and an instance of the field's declaring class.
-  mirror::Object* o = nullptr;
+  ObjPtr<mirror::Object> o;
   if (!CheckReceiver(soa, javaObj, &f, &o)) {
     DCHECK(soa.Self()->IsExceptionPending());
     return;
   }
-  mirror::Class* field_type;
+  ObjPtr<mirror::Class> field_type;
   const char* field_type_desciptor = f->GetArtField()->GetTypeDescriptor();
   Primitive::Type field_prim_type = Primitive::GetType(field_type_desciptor[0]);
   if (field_prim_type == Primitive::kPrimNot) {
@@ -328,7 +335,7 @@
   ObjPtr<mirror::Object> boxed_value = soa.Decode<mirror::Object>(javaValue);
   JValue unboxed_value;
   if (!UnboxPrimitiveForField(boxed_value,
-                              MakeObjPtr(field_type),
+                              field_type,
                               f->GetArtField(),
                               &unboxed_value)) {
     DCHECK(soa.Self()->IsExceptionPending());
@@ -343,11 +350,13 @@
 }
 
 template<Primitive::Type kPrimitiveType>
-static void SetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj,
+static void SetPrimitiveField(JNIEnv* env,
+                              jobject javaField,
+                              jobject javaObj,
                               const JValue& new_value) {
   ScopedFastNativeObjectAccess soa(env);
-  mirror::Field* f = soa.Decode<mirror::Field>(javaField).Ptr();
-  mirror::Object* o = nullptr;
+  ObjPtr<mirror::Field> f = soa.Decode<mirror::Field>(javaField);
+  ObjPtr<mirror::Object> o;
   if (!CheckReceiver(soa, javaObj, &f, &o)) {
     return;
   }
@@ -439,11 +448,10 @@
   ArtField* field = soa.Decode<mirror::Field>(javaField)->GetArtField();
   if (field->GetDeclaringClass()->IsProxyClass()) {
     // Return an empty array instead of a null pointer.
-    mirror::Class* annotation_array_class =
-        soa.Decode<mirror::Class>(
-            WellKnownClasses::java_lang_annotation_Annotation__array).Ptr();
-    mirror::ObjectArray<mirror::Object>* empty_array =
-        mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), annotation_array_class, 0);
+    ObjPtr<mirror::Class> annotation_array_class =
+        soa.Decode<mirror::Class>(WellKnownClasses::java_lang_annotation_Annotation__array);
+    ObjPtr<mirror::ObjectArray<mirror::Object>> empty_array =
+        mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), annotation_array_class.Ptr(), 0);
     return soa.AddLocalReference<jobjectArray>(empty_array);
   }
   return soa.AddLocalReference<jobjectArray>(annotations::GetAnnotationsForField(field));
@@ -458,7 +466,8 @@
   return soa.AddLocalReference<jobjectArray>(annotations::GetSignatureAnnotationForField(field));
 }
 
-static jboolean Field_isAnnotationPresentNative(JNIEnv* env, jobject javaField,
+static jboolean Field_isAnnotationPresentNative(JNIEnv* env,
+                                                jobject javaField,
                                                 jclass annotationType) {
   ScopedFastNativeObjectAccess soa(env);
   StackHandleScope<1> hs(soa.Self());
diff --git a/runtime/obj_ptr.h b/runtime/obj_ptr.h
index beb4d33..6688ba7 100644
--- a/runtime/obj_ptr.h
+++ b/runtime/obj_ptr.h
@@ -18,6 +18,7 @@
 #define ART_RUNTIME_OBJ_PTR_H_
 
 #include <ostream>
+#include <type_traits>
 
 #include "base/mutex.h"  // For Locks::mutator_lock_.
 #include "globals.h"
@@ -45,14 +46,23 @@
 
   template <typename Type>
   ALWAYS_INLINE ObjPtr(Type* ptr) REQUIRES_SHARED(Locks::mutator_lock_)
-      : reference_(Encode(static_cast<MirrorType*>(ptr))) {}
+      : reference_(Encode(static_cast<MirrorType*>(ptr))) {
+    static_assert(std::is_base_of<MirrorType, Type>::value,
+                  "Input type must be a subtype of the ObjPtr type");
+  }
 
   template <typename Type>
-  ALWAYS_INLINE ObjPtr(const ObjPtr<Type>& other) REQUIRES_SHARED(Locks::mutator_lock_)
-      : reference_(Encode(static_cast<MirrorType*>(other.Ptr()))) {}
+  ALWAYS_INLINE ObjPtr(const ObjPtr<Type, kPoison>& other) REQUIRES_SHARED(Locks::mutator_lock_)
+      : reference_(Encode(static_cast<MirrorType*>(other.Ptr()))) {
+    static_assert(std::is_base_of<MirrorType, Type>::value,
+                  "Input type must be a subtype of the ObjPtr type");
+  }
 
   template <typename Type>
-  ALWAYS_INLINE ObjPtr& operator=(const ObjPtr& other) {
+  ALWAYS_INLINE ObjPtr& operator=(const ObjPtr<Type, kPoison>& other)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    static_assert(std::is_base_of<MirrorType, Type>::value,
+                  "Input type must be a subtype of the ObjPtr type");
     reference_ = Encode(static_cast<MirrorType*>(other.Ptr()));
     return *this;
   }
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index a1a2361..bebc2fc 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -38,7 +38,11 @@
 
 #include "art_jvmti.h"
 #include "jni_env_ext-inl.h"
+#include "object_tagging.h"
+#include "obj_ptr-inl.h"
 #include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread_list.h"
 #include "transform.h"
 
 // TODO Remove this at some point by annotating all the methods. It was put in to make the skeleton
@@ -47,6 +51,8 @@
 
 namespace openjdkjvmti {
 
+ObjectTagTable gObjectTagTable;
+
 class JvmtiFunctions {
  private:
   static bool IsValidEnv(jvmtiEnv* env) {
@@ -270,11 +276,42 @@
   }
 
   static jvmtiError GetTag(jvmtiEnv* env, jobject object, jlong* tag_ptr) {
-    return ERR(NOT_IMPLEMENTED);
+    if (object == nullptr || tag_ptr == nullptr) {
+      return ERR(NULL_POINTER);
+    }
+
+    JNIEnv* jni_env = GetJniEnv(env);
+    if (jni_env == nullptr) {
+      return ERR(INTERNAL);
+    }
+
+    art::ScopedObjectAccess soa(jni_env);
+    art::ObjPtr<art::mirror::Object> obj = soa.Decode<art::mirror::Object>(object);
+    if (!gObjectTagTable.GetTag(obj.Ptr(), tag_ptr)) {
+      *tag_ptr = 0;
+    }
+
+    return ERR(NONE);
   }
 
   static jvmtiError SetTag(jvmtiEnv* env, jobject object, jlong tag) {
-    return ERR(NOT_IMPLEMENTED);
+    if (object == nullptr) {
+      return ERR(NULL_POINTER);
+    }
+
+    JNIEnv* jni_env = GetJniEnv(env);
+    if (jni_env == nullptr) {
+      return ERR(INTERNAL);
+    }
+
+    art::ScopedObjectAccess soa(jni_env);
+    art::ObjPtr<art::mirror::Object> obj = soa.Decode<art::mirror::Object>(object);
+    gObjectTagTable.Remove(obj.Ptr(), /* tag* */ nullptr);
+    if (tag != 0) {
+      gObjectTagTable.Add(obj.Ptr(), tag);
+    }
+
+    return ERR(NONE);
   }
 
   static jvmtiError GetObjectsWithTags(jvmtiEnv* env,
diff --git a/runtime/openjdkjvmti/object_tagging.h b/runtime/openjdkjvmti/object_tagging.h
new file mode 100644
index 0000000..e276c52
--- /dev/null
+++ b/runtime/openjdkjvmti/object_tagging.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_OBJECT_TAGGING_H_
+#define ART_RUNTIME_OPENJDKJVMTI_OBJECT_TAGGING_H_
+
+#include "base/mutex.h"
+#include "gc/system_weak.h"
+#include "gc_root-inl.h"
+#include "mirror/object.h"
+#include "thread-inl.h"
+
+namespace openjdkjvmti {
+
+class ObjectTagTable : public art::gc::SystemWeakHolder {
+ public:
+  ObjectTagTable() : art::gc::SystemWeakHolder(art::LockLevel::kAllocTrackerLock) {
+  }
+
+  void Add(art::mirror::Object* obj, jlong tag)
+      REQUIRES_SHARED(art::Locks::mutator_lock_)
+      REQUIRES(!allow_disallow_lock_) {
+    art::Thread* self = art::Thread::Current();
+    art::MutexLock mu(self, allow_disallow_lock_);
+    Wait(self);
+
+    if (first_free_ == tagged_objects_.size()) {
+      tagged_objects_.push_back(Entry(art::GcRoot<art::mirror::Object>(obj), tag));
+      first_free_++;
+    } else {
+      DCHECK_LT(first_free_, tagged_objects_.size());
+      DCHECK(tagged_objects_[first_free_].first.IsNull());
+      tagged_objects_[first_free_] = Entry(art::GcRoot<art::mirror::Object>(obj), tag);
+      // TODO: scan for free elements.
+      first_free_ = tagged_objects_.size();
+    }
+  }
+
+  bool GetTag(art::mirror::Object* obj, jlong* result)
+      REQUIRES_SHARED(art::Locks::mutator_lock_)
+      REQUIRES(!allow_disallow_lock_) {
+    art::Thread* self = art::Thread::Current();
+    art::MutexLock mu(self, allow_disallow_lock_);
+    Wait(self);
+
+    for (const auto& pair : tagged_objects_) {
+      if (pair.first.Read(nullptr) == obj) {
+        *result = pair.second;
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  bool Remove(art::mirror::Object* obj, jlong* tag)
+      REQUIRES_SHARED(art::Locks::mutator_lock_)
+      REQUIRES(!allow_disallow_lock_) {
+    art::Thread* self = art::Thread::Current();
+    art::MutexLock mu(self, allow_disallow_lock_);
+    Wait(self);
+
+    for (auto it = tagged_objects_.begin(); it != tagged_objects_.end(); ++it) {
+      if (it->first.Read(nullptr) == obj) {
+        if (tag != nullptr) {
+          *tag = it->second;
+        }
+        it->first = art::GcRoot<art::mirror::Object>(nullptr);
+
+        size_t index = it - tagged_objects_.begin();
+        if (index < first_free_) {
+          first_free_ = index;
+        }
+
+        // TODO: compaction.
+
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  void Sweep(art::IsMarkedVisitor* visitor)
+      REQUIRES_SHARED(art::Locks::mutator_lock_)
+      REQUIRES(!allow_disallow_lock_) {
+    art::Thread* self = art::Thread::Current();
+    art::MutexLock mu(self, allow_disallow_lock_);
+
+    for (auto it = tagged_objects_.begin(); it != tagged_objects_.end(); ++it) {
+      if (!it->first.IsNull()) {
+        art::mirror::Object* original_obj = it->first.Read();
+        art::mirror::Object* target_obj = visitor->IsMarked(original_obj);
+        if (original_obj != target_obj) {
+          it->first = art::GcRoot<art::mirror::Object>(target_obj);
+
+          if (target_obj == nullptr) {
+            HandleNullSweep(it->second);
+          }
+        }
+      } else {
+        size_t index = it - tagged_objects_.begin();
+        if (index < first_free_) {
+          first_free_ = index;
+        }
+      }
+    }
+  }
+
+ private:
+  using Entry = std::pair<art::GcRoot<art::mirror::Object>, jlong>;
+
+  void HandleNullSweep(jlong tag ATTRIBUTE_UNUSED) {
+    // TODO: Handle deallocation reporting here. We'll have to enqueue tags temporarily, as we
+    //       probably shouldn't call the callbacks directly (to avoid any issues).
+  }
+
+  std::vector<Entry> tagged_objects_ GUARDED_BY(allow_disallow_lock_);
+  size_t first_free_ = 0;
+};
+
+}  // namespace openjdkjvmti
+
+#endif  // ART_RUNTIME_OPENJDKJVMTI_OBJECT_TAGGING_H_
diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc
index 1119ccf..84985c2 100644
--- a/runtime/proxy_test.cc
+++ b/runtime/proxy_test.cc
@@ -180,7 +180,7 @@
   ArtField* field = &static_fields->At(0);
   EXPECT_STREQ("interfaces", field->GetName());
   EXPECT_STREQ("[Ljava/lang/Class;", field->GetTypeDescriptor());
-  EXPECT_OBJ_PTR_EQ(MakeObjPtr(interfacesFieldClass.Get()), field->GetType<true>());
+  EXPECT_OBJ_PTR_EQ(interfacesFieldClass.Get(), field->GetType<true>());
   std::string temp;
   EXPECT_STREQ("L$Proxy1234;", field->GetDeclaringClass()->GetDescriptor(&temp));
   EXPECT_FALSE(field->IsPrimitiveType());
@@ -189,7 +189,7 @@
   field = &static_fields->At(1);
   EXPECT_STREQ("throws", field->GetName());
   EXPECT_STREQ("[[Ljava/lang/Class;", field->GetTypeDescriptor());
-  EXPECT_OBJ_PTR_EQ(MakeObjPtr(throwsFieldClass.Get()), field->GetType<true>());
+  EXPECT_OBJ_PTR_EQ(throwsFieldClass.Get(), field->GetType<true>());
   EXPECT_STREQ("L$Proxy1234;", field->GetDeclaringClass()->GetDescriptor(&temp));
   EXPECT_FALSE(field->IsPrimitiveType());
 }
@@ -224,10 +224,10 @@
   ASSERT_TRUE(static_fields1 != nullptr);
   ASSERT_EQ(2u, static_fields1->size());
 
-  EXPECT_OBJ_PTR_EQ(static_fields0->At(0).GetDeclaringClass(), MakeObjPtr(proxyClass0.Get()));
-  EXPECT_OBJ_PTR_EQ(static_fields0->At(1).GetDeclaringClass(), MakeObjPtr(proxyClass0.Get()));
-  EXPECT_OBJ_PTR_EQ(static_fields1->At(0).GetDeclaringClass(), MakeObjPtr(proxyClass1.Get()));
-  EXPECT_OBJ_PTR_EQ(static_fields1->At(1).GetDeclaringClass(), MakeObjPtr(proxyClass1.Get()));
+  EXPECT_OBJ_PTR_EQ(static_fields0->At(0).GetDeclaringClass(), proxyClass0.Get());
+  EXPECT_OBJ_PTR_EQ(static_fields0->At(1).GetDeclaringClass(), proxyClass0.Get());
+  EXPECT_OBJ_PTR_EQ(static_fields1->At(0).GetDeclaringClass(), proxyClass1.Get());
+  EXPECT_OBJ_PTR_EQ(static_fields1->At(1).GetDeclaringClass(), proxyClass1.Get());
 
   ASSERT_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
   ASSERT_FALSE(Runtime::Current()->IsActiveTransaction());
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index de003e5..d34b701 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -676,8 +676,7 @@
   }
 
   // Box if necessary and return.
-  return soa.AddLocalReference<jobject>(
-      BoxPrimitive(Primitive::GetType(shorty[0]), result).Ptr());
+  return soa.AddLocalReference<jobject>(BoxPrimitive(Primitive::GetType(shorty[0]), result));
 }
 
 ObjPtr<mirror::Object> BoxPrimitive(Primitive::Type src_class, const JValue& value) {
@@ -911,7 +910,7 @@
   IndirectRef ref = reinterpret_cast<IndirectRef>(obj);
   IndirectRefKind kind = GetIndirectRefKind(ref);
   if (kind == kLocal) {
-    self->GetJniEnv()->locals.Update(obj, result.Ptr());
+    self->GetJniEnv()->locals.Update(obj, result);
   } else if (kind == kHandleScopeOrInvalid) {
     LOG(FATAL) << "Unsupported UpdateReference for kind kHandleScopeOrInvalid";
   } else if (kind == kGlobal) {
diff --git a/runtime/runtime-inl.h b/runtime/runtime-inl.h
index 2eb0bf7..8346550 100644
--- a/runtime/runtime-inl.h
+++ b/runtime/runtime-inl.h
@@ -21,11 +21,12 @@
 
 #include "art_method.h"
 #include "class_linker.h"
+#include "obj_ptr-inl.h"
 #include "read_barrier-inl.h"
 
 namespace art {
 
-inline bool Runtime::IsClearedJniWeakGlobal(mirror::Object* obj) {
+inline bool Runtime::IsClearedJniWeakGlobal(ObjPtr<mirror::Object> obj) {
   return obj == GetClearedJniWeakGlobal();
 }
 
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 84c6b6f..5a95f78 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -33,6 +33,7 @@
 #include "instrumentation.h"
 #include "jobject_comparator.h"
 #include "method_reference.h"
+#include "obj_ptr.h"
 #include "object_callbacks.h"
 #include "offsets.h"
 #include "process_state.h"
@@ -292,7 +293,7 @@
   }
 
   // Is the given object the special object used to mark a cleared JNI weak global?
-  bool IsClearedJniWeakGlobal(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_);
+  bool IsClearedJniWeakGlobal(ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Get the special object used to mark a cleared JNI weak global.
   mirror::Object* GetClearedJniWeakGlobal() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/scoped_thread_state_change-inl.h b/runtime/scoped_thread_state_change-inl.h
index 1d9f132..ac25757 100644
--- a/runtime/scoped_thread_state_change-inl.h
+++ b/runtime/scoped_thread_state_change-inl.h
@@ -72,19 +72,13 @@
 }
 
 template<typename T>
-inline T ScopedObjectAccessAlreadyRunnable::AddLocalReference(mirror::Object* obj) const {
+inline T ScopedObjectAccessAlreadyRunnable::AddLocalReference(ObjPtr<mirror::Object> obj) const {
   Locks::mutator_lock_->AssertSharedHeld(Self());
   DCHECK(IsRunnable());  // Don't work with raw objects in non-runnable states.
   DCHECK_NE(obj, Runtime::Current()->GetClearedJniWeakGlobal());
   return obj == nullptr ? nullptr : Env()->AddLocalReference<T>(obj);
 }
 
-template<typename T, typename MirrorType, bool kPoison>
-inline T ScopedObjectAccessAlreadyRunnable::AddLocalReference(
-    ObjPtr<MirrorType, kPoison> obj) const {
-  return AddLocalReference<T>(obj.Ptr());
-}
-
 template<typename T, bool kPoison>
 inline ObjPtr<T, kPoison> ScopedObjectAccessAlreadyRunnable::Decode(jobject obj) const {
   Locks::mutator_lock_->AssertSharedHeld(Self());
diff --git a/runtime/scoped_thread_state_change.h b/runtime/scoped_thread_state_change.h
index 175bec5..04fd914 100644
--- a/runtime/scoped_thread_state_change.h
+++ b/runtime/scoped_thread_state_change.h
@@ -87,13 +87,8 @@
    * This will be called on otherwise unreferenced objects. We cannot do GC allocations here, and
    * it's best if we don't grab a mutex.
    */
-  template<typename T, typename MirrorType, bool kPoison = kIsDebugBuild>
-  T AddLocalReference(ObjPtr<MirrorType, kPoison> obj) const
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
-  // TODO: Delete
   template<typename T>
-  T AddLocalReference(mirror::Object* obj) const
+  T AddLocalReference(ObjPtr<mirror::Object> obj) const
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<typename T, bool kPoison = kIsDebugBuild>
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index d75a788..6c3811b 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -312,6 +312,37 @@
   }
 }
 
+inline bool Thread::ModifySuspendCount(Thread* self,
+                                       int delta,
+                                       AtomicInteger* suspend_barrier,
+                                       bool for_debugger) {
+  if (delta > 0 && ((kUseReadBarrier && this != self) || suspend_barrier != nullptr)) {
+    // When delta > 0 (requesting a suspend), ModifySuspendCountInternal() may fail either if
+    // active_suspend_barriers is full or we are in the middle of a thread flip. Retry in a loop.
+    while (true) {
+      if (LIKELY(ModifySuspendCountInternal(self, delta, suspend_barrier, for_debugger))) {
+        return true;
+      } else {
+        // Failure means the list of active_suspend_barriers is full or we are in the middle of a
+        // thread flip, we should release the thread_suspend_count_lock_ (to avoid deadlock) and
+        // wait till the target thread has executed or Thread::PassActiveSuspendBarriers() or the
+        // flip function. Note that we could not simply wait for the thread to change to a suspended
+        // state, because it might need to run checkpoint function before the state change or
+        // resumes from the resume_cond_, which also needs thread_suspend_count_lock_.
+        //
+        // The list of active_suspend_barriers is very unlikely to be full since more than
+        // kMaxSuspendBarriers threads need to execute SuspendAllInternal() simultaneously, and
+        // target thread stays in kRunnable in the mean time.
+        Locks::thread_suspend_count_lock_->ExclusiveUnlock(self);
+        NanoSleep(100000);
+        Locks::thread_suspend_count_lock_->ExclusiveLock(self);
+      }
+    }
+  } else {
+    return ModifySuspendCountInternal(self, delta, suspend_barrier, for_debugger);
+  }
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_THREAD_INL_H_
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 80542e8..7335e40 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -991,8 +991,10 @@
   LOG(FATAL) << ss.str();
 }
 
-bool Thread::ModifySuspendCount(Thread* self, int delta, AtomicInteger* suspend_barrier,
-                                bool for_debugger) {
+bool Thread::ModifySuspendCountInternal(Thread* self,
+                                        int delta,
+                                        AtomicInteger* suspend_barrier,
+                                        bool for_debugger) {
   if (kIsDebugBuild) {
     DCHECK(delta == -1 || delta == +1 || delta == -tls32_.debug_suspend_count)
           << delta << " " << tls32_.debug_suspend_count << " " << this;
@@ -1007,6 +1009,12 @@
     return false;
   }
 
+  if (kUseReadBarrier && delta > 0 && this != self && tlsPtr_.flip_function != nullptr) {
+    // Force retry of a suspend request if it's in the middle of a thread flip to avoid a
+    // deadlock. b/31683379.
+    return false;
+  }
+
   uint16_t flags = kSuspendRequest;
   if (delta > 0 && suspend_barrier != nullptr) {
     uint32_t available_barrier = kMaxSuspendBarriers;
@@ -1854,7 +1862,7 @@
   }
   IndirectRef ref = reinterpret_cast<IndirectRef>(obj);
   IndirectRefKind kind = GetIndirectRefKind(ref);
-  mirror::Object* result;
+  ObjPtr<mirror::Object> result;
   bool expect_null = false;
   // The "kinds" below are sorted by the frequency we expect to encounter them.
   if (kind == kLocal) {
@@ -1867,7 +1875,7 @@
     if (LIKELY(HandleScopeContains(obj))) {
       // Read from handle scope.
       result = reinterpret_cast<StackReference<mirror::Object>*>(obj)->AsMirrorPtr();
-      VerifyObject(result);
+      VerifyObject(result.Ptr());
     } else {
       tlsPtr_.jni_env->vm->JniAbortF(nullptr, "use of invalid jobject %p", obj);
       expect_null = true;
@@ -1889,7 +1897,7 @@
     tlsPtr_.jni_env->vm->JniAbortF(nullptr, "use of deleted %s %p",
                                    ToStr<IndirectRefKind>(kind).c_str(), obj);
   }
-  return result;
+  return result.Ptr();
 }
 
 bool Thread::IsJWeakCleared(jweak obj) const {
diff --git a/runtime/thread.h b/runtime/thread.h
index f2c22d1..97053de 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -226,7 +226,13 @@
         (state_and_flags.as_struct.flags & kSuspendRequest) != 0;
   }
 
-  bool ModifySuspendCount(Thread* self, int delta, AtomicInteger* suspend_barrier, bool for_debugger)
+  // If delta > 0 and (this != self or suspend_barrier is not null), this function may temporarily
+  // release thread_suspend_count_lock_ internally.
+  ALWAYS_INLINE
+  bool ModifySuspendCount(Thread* self,
+                          int delta,
+                          AtomicInteger* suspend_barrier,
+                          bool for_debugger)
       REQUIRES(Locks::thread_suspend_count_lock_);
 
   bool RequestCheckpoint(Closure* function)
@@ -1220,6 +1226,12 @@
     is_sensitive_thread_hook_ = is_sensitive_thread_hook;
   }
 
+  bool ModifySuspendCountInternal(Thread* self,
+                                  int delta,
+                                  AtomicInteger* suspend_barrier,
+                                  bool for_debugger)
+      REQUIRES(Locks::thread_suspend_count_lock_);
+
   // 32 bits of atomically changed state and flags. Keeping as 32 bits allows and atomic CAS to
   // change from being Suspended to Runnable without a suspend request occurring.
   union PACKED(4) StateAndFlags {
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 6f952c4..eba6666 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -590,24 +590,7 @@
         continue;
       }
       VLOG(threads) << "requesting thread suspend: " << *thread;
-      while (true) {
-        if (LIKELY(thread->ModifySuspendCount(self, +1, &pending_threads, debug_suspend))) {
-          break;
-        } else {
-          // Failure means the list of active_suspend_barriers is full, we should release the
-          // thread_suspend_count_lock_ (to avoid deadlock) and wait till the target thread has
-          // executed Thread::PassActiveSuspendBarriers(). Note that we could not simply wait for
-          // the thread to change to a suspended state, because it might need to run checkpoint
-          // function before the state change, which also needs thread_suspend_count_lock_.
-
-          // This is very unlikely to happen since more than kMaxSuspendBarriers threads need to
-          // execute SuspendAllInternal() simultaneously, and target thread stays in kRunnable
-          // in the mean time.
-          Locks::thread_suspend_count_lock_->ExclusiveUnlock(self);
-          NanoSleep(100000);
-          Locks::thread_suspend_count_lock_->ExclusiveLock(self);
-        }
-      }
+      thread->ModifySuspendCount(self, +1, &pending_threads, debug_suspend);
 
       // Must install the pending_threads counter first, then check thread->IsSuspend() and clear
       // the counter. Otherwise there's a race with Thread::TransitionFromRunnableToSuspended()
diff --git a/test/903-hello-tagging/build b/test/903-hello-tagging/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/903-hello-tagging/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-build "$@" --experimental agents
diff --git a/test/903-hello-tagging/expected.txt b/test/903-hello-tagging/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/903-hello-tagging/expected.txt
diff --git a/test/903-hello-tagging/info.txt b/test/903-hello-tagging/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/903-hello-tagging/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/903-hello-tagging/run b/test/903-hello-tagging/run
new file mode 100755
index 0000000..5e3c0bd
--- /dev/null
+++ b/test/903-hello-tagging/run
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+lib=tiagentd
+if  [[ "$@" == *"-O"* ]]; then
+  agent=libtiagent.so
+  plugin=libopenjdkjvmti.so
+  lib=tiagent
+fi
+
+if [[ "$@" == *"--jvm"* ]]; then
+  arg="jvm"
+else
+  arg="art"
+fi
+
+if [[ "$@" != *"--debuggable"* ]]; then
+  other_args=" -Xcompiler-option --debuggable "
+else
+  other_args=""
+fi
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --runtime-option -agentpath:${agent}=903-hello-tagging,${arg} \
+                   --android-runtime-option -Xplugin:${plugin} \
+                   ${other_args} \
+                   --args ${lib}
diff --git a/test/903-hello-tagging/src/Main.java b/test/903-hello-tagging/src/Main.java
new file mode 100644
index 0000000..2856a39
--- /dev/null
+++ b/test/903-hello-tagging/src/Main.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.ref.WeakReference;
+
+public class Main {
+  public static void main(String[] args) {
+    System.loadLibrary(args[1]);
+    doTest();
+  }
+
+  public static void doTest() {
+    WeakReference<Object> weak = test();
+
+    Runtime.getRuntime().gc();
+    Runtime.getRuntime().gc();
+
+    if (weak.get() != null) {
+      throw new RuntimeException("WeakReference not cleared");
+    }
+  }
+
+  private static WeakReference<Object> test() {
+    Object o1 = new Object();
+    setTag(o1, 1);
+
+    Object o2 = new Object();
+    setTag(o2, 2);
+
+    checkTag(o1, 1);
+    checkTag(o2, 2);
+
+    Runtime.getRuntime().gc();
+    Runtime.getRuntime().gc();
+
+    checkTag(o1, 1);
+    checkTag(o2, 2);
+
+    Runtime.getRuntime().gc();
+    Runtime.getRuntime().gc();
+
+    setTag(o1, 10);
+    setTag(o2, 20);
+
+    checkTag(o1, 10);
+    checkTag(o2, 20);
+
+    return new WeakReference<Object>(o1);
+  }
+
+  private static void checkTag(Object o, long expectedTag) {
+    long tag = getTag(o);
+    if (expectedTag != tag) {
+      throw new RuntimeException("Unexpected tag " + tag + ", expected " + expectedTag);
+    }
+  }
+
+  private static native void setTag(Object o, long tag);
+  private static native long getTag(Object o);
+}
diff --git a/test/903-hello-tagging/tagging.cc b/test/903-hello-tagging/tagging.cc
new file mode 100644
index 0000000..8ccdf49
--- /dev/null
+++ b/test/903-hello-tagging/tagging.cc
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "tagging.h"
+
+#include <iostream>
+#include <pthread.h>
+#include <stdio.h>
+#include <vector>
+
+#include "art_method-inl.h"
+#include "base/logging.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "utils.h"
+
+namespace art {
+namespace Test903HelloTagging {
+
+static jvmtiEnv* jvmti_env;
+
+extern "C" JNIEXPORT void JNICALL Java_Main_setTag(JNIEnv* env ATTRIBUTE_UNUSED,
+                                                   jclass,
+                                                   jobject obj,
+                                                   jlong tag) {
+  jvmtiError ret = jvmti_env->SetTag(obj, tag);
+  if (ret != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(ret, &err);
+    printf("Error setting tag: %s\n", err);
+  }
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_Main_getTag(JNIEnv* env ATTRIBUTE_UNUSED,
+                                                    jclass,
+                                                    jobject obj) {
+  jlong tag = 0;
+  jvmtiError ret = jvmti_env->GetTag(obj, &tag);
+  if (ret != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(ret, &err);
+    printf("Error getting tag: %s\n", err);
+  }
+  return tag;
+}
+
+// Don't do anything
+jint OnLoad(JavaVM* vm,
+            char* options ATTRIBUTE_UNUSED,
+            void* reserved ATTRIBUTE_UNUSED) {
+  if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+    printf("Unable to get jvmti env!\n");
+    return 1;
+  }
+  return 0;
+}
+
+}  // namespace Test903HelloTagging
+}  // namespace art
+
diff --git a/test/903-hello-tagging/tagging.h b/test/903-hello-tagging/tagging.h
new file mode 100644
index 0000000..f062d44
--- /dev/null
+++ b/test/903-hello-tagging/tagging.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_TEST_903_HELLO_TAGGING_TAGGING_H_
+#define ART_TEST_903_HELLO_TAGGING_TAGGING_H_
+
+#include <jni.h>
+
+namespace art {
+namespace Test903HelloTagging {
+
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+
+}  // namespace Test903HelloTagging
+}  // namespace art
+
+#endif  // ART_TEST_903_HELLO_TAGGING_TAGGING_H_
diff --git a/test/Android.bp b/test/Android.bp
index 628f377..d17261c 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -245,6 +245,7 @@
         "ti-agent/common_load.cc",
         "901-hello-ti-agent/basics.cc",
         "902-hello-transformation/transform.cc",
+        "903-hello-tagging/tagging.cc",
     ],
     shared_libs: [
         "libart",
@@ -263,9 +264,11 @@
         "ti-agent/common_load.cc",
         "901-hello-ti-agent/basics.cc",
         "902-hello-transformation/transform.cc",
+        "903-hello-tagging/tagging.cc",
     ],
     shared_libs: [
         "libartd",
+        "libbase",
         "libopenjdkjvmtid",
     ],
 }
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index 53bb153..4c7df97 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -25,6 +25,7 @@
 
 #include "901-hello-ti-agent/basics.h"
 #include "902-hello-transformation/transform.h"
+#include "903-hello-tagging/tagging.h"
 
 namespace art {
 
@@ -41,6 +42,7 @@
 AgentLib agents[] = {
   { "901-hello-ti-agent", Test901HelloTi::OnLoad, nullptr },
   { "902-hello-transformation", Test902HelloTransformation::OnLoad, nullptr },
+  { "903-hello-tagging", Test903HelloTagging::OnLoad, nullptr },
 };
 
 static AgentLib* FindAgent(char* name) {
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index 7b5e9ed..53fe8fe 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -233,11 +233,5 @@
   modes: [device],
   names: ["libcore.java.lang.ProcessBuilderTest#testRedirectInherit",
           "libcore.java.lang.ProcessBuilderTest#testRedirect_nullStreams"]
-},
-{
-  description: "Sometimes timeouts",
-  result: EXEC_FAILED,
-  bug: 31258002,
-  names: ["libcore.net.NetworkSecurityPolicyTest#testCleartextTrafficPolicyWithJarHttpURLConnection"]
 }
 ]