Revert "Revert "Add dex file writer to dexlayout tool.""

This reverts commit fd1a6c2a08ca3e2476b7424b9b0fa58e73b29e87.

Fixed output being clobbered during DexLayoutTest.DexFileOutput.
Option added to put dex output file in scratch directory.

Bug: 29921113
Test: mm test-art-host-gtest-dexlayout_test

Change-Id: I9e6b139cf06aaa39c83ad1e74329db266464a8e4
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;
 }