Finish dexlayout dumping of dex files and added test.

The test runs dexdump and dexlayout over the first core library jar file
and diffs the output, checking that they're the same. More dex files
will be added later for better coverage.

Bug: 29921113
Test: mm test-art-host-gtest-dexlayout_test
(cherry-picked from commit a378e88db648cf19e771714dd1059cf3d41d4753)

Change-Id: Idfd8ac9f069885b675a4a2f0c70aef2cbef5a9b8
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 9ec072f..08b56d0 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -126,6 +126,19 @@
   $(TARGET_CORE_IMAGE_default_no-pic_32) \
   dexdump2
 
+# The dexlayout test requires an image and the dexlayout utility.
+# TODO: rename into dexdump when migration completes
+ART_GTEST_dexlayout_test_HOST_DEPS := \
+  $(HOST_CORE_IMAGE_default_no-pic_64) \
+  $(HOST_CORE_IMAGE_default_no-pic_32) \
+  $(HOST_OUT_EXECUTABLES)/dexlayout \
+  $(HOST_OUT_EXECUTABLES)/dexdump2
+ART_GTEST_dexlayout_test_TARGET_DEPS := \
+  $(TARGET_CORE_IMAGE_default_no-pic_64) \
+  $(TARGET_CORE_IMAGE_default_no-pic_32) \
+  dexlayout \
+  dexdump2
+
 # The dexlist test requires an image and the dexlist utility.
 ART_GTEST_dexlist_test_HOST_DEPS := \
   $(HOST_CORE_IMAGE_default_no-pic_64) \
@@ -170,6 +183,7 @@
 RUNTIME_GTEST_COMMON_SRC_FILES := \
   cmdline/cmdline_parser_test.cc \
   dexdump/dexdump_test.cc \
+  dexlayout/dexlayout_test.cc \
   dexlist/dexlist_test.cc \
   dex2oat/dex2oat_test.cc \
   imgdiag/imgdiag_test.cc \
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
index 40e60f7..0ed040e 100644
--- a/dexlayout/dex_ir.cc
+++ b/dexlayout/dex_ir.cc
@@ -54,9 +54,10 @@
 static void GetLocalsCb(void* context, const DexFile::LocalInfo& entry) {
   DebugInfoItem* debug_info = reinterpret_cast<DebugInfoItem*>(context);
   std::vector<std::unique_ptr<LocalInfo>>& locals = debug_info->GetLocalInfo();
+  const char* name = entry.name_ != nullptr ? entry.name_ : "(null)";
   const char* signature = entry.signature_ != nullptr ? entry.signature_ : "";
   locals.push_back(std::unique_ptr<LocalInfo>(
-      new LocalInfo(entry.name_, entry.descriptor_, signature, entry.start_address_,
+      new LocalInfo(name, entry.descriptor_, signature, entry.start_address_,
                     entry.end_address_, entry.reg_)));
 }
 }  // namespace
@@ -205,7 +206,7 @@
 ClassDef::ClassDef(const DexFile::ClassDef& disk_class_def, Header& header) {
   class_type_ = header.TypeIds()[disk_class_def.class_idx_].get();
   access_flags_ = disk_class_def.access_flags_;
-  superclass_ = header.TypeIds()[disk_class_def.superclass_idx_].get();
+  superclass_ = header.GetTypeIdOrNullPtr(disk_class_def.superclass_idx_);
 
   const DexFile::TypeList* type_list = header.GetDexFile().GetInterfacesList(disk_class_def);
   interfaces_offset_ = disk_class_def.interfaces_off_;
@@ -214,11 +215,7 @@
       interfaces_.push_back(header.TypeIds()[type_list->GetTypeItem(index).type_idx_].get());
     }
   }
-  if (disk_class_def.source_file_idx_ == DexFile::kDexNoIndex) {
-    source_file_ = nullptr;
-  } else {
-    source_file_ = header.StringIds()[disk_class_def.source_file_idx_].get();
-  }
+  source_file_ = header.GetStringIdOrNullPtr(disk_class_def.source_file_idx_);
   // Annotations.
   const DexFile::AnnotationsDirectoryItem* disk_annotations_directory_item =
       header.GetDexFile().GetAnnotationsDirectory(disk_class_def);
@@ -262,51 +259,37 @@
     }
     // Direct methods.
     for (uint32_t i = 0; cdii.HasNextDirectMethod(); i++, cdii.Next()) {
-      MethodId* method_item = header.MethodIds()[cdii.GetMemberIndex()].get();
-      uint32_t access_flags = cdii.GetRawMemberAccessFlags();
-      const DexFile::CodeItem* disk_code_item = cdii.GetMethodCodeItem();
-      CodeItem* code_item = nullptr;
-      DebugInfoItem* debug_info = nullptr;
-      if (disk_code_item != nullptr) {
-        code_item = new CodeItem(*disk_code_item, header);
-        code_item->SetOffset(cdii.GetMethodCodeItemOffset());
-        debug_info = code_item->DebugInfo();
-      }
-      if (debug_info != nullptr) {
-        // TODO: Fix debug local info.
-        // bool is_static = (access_flags & kAccStatic) != 0;
-        // header.GetDexFile().DecodeDebugLocalInfo(
-        //       disk_code_item, is_static, cdii.GetMemberIndex(), GetLocalsCb, debug_info);
-        header.GetDexFile().DecodeDebugPositionInfo(disk_code_item, GetPositionsCb, debug_info);
-      }
       class_data_.DirectMethods().push_back(
-          std::unique_ptr<MethodItem>(new MethodItem(access_flags, method_item, code_item)));
+          std::unique_ptr<MethodItem>(GenerateMethodItem(header, cdii)));
     }
     // Virtual methods.
     for (uint32_t i = 0; cdii.HasNextVirtualMethod(); i++, cdii.Next()) {
-      MethodId* method_item = header.MethodIds()[cdii.GetMemberIndex()].get();
-      uint32_t access_flags = cdii.GetRawMemberAccessFlags();
-      const DexFile::CodeItem* disk_code_item = cdii.GetMethodCodeItem();
-      CodeItem* code_item = nullptr;
-      DebugInfoItem* debug_info = nullptr;
-      if (disk_code_item != nullptr) {
-        code_item = new CodeItem(*disk_code_item, header);
-        code_item->SetOffset(cdii.GetMethodCodeItemOffset());
-        debug_info = code_item->DebugInfo();
-      }
-      if (debug_info != nullptr) {
-        // TODO: Fix debug local info.
-        // bool is_static = (access_flags & kAccStatic) != 0;
-        // header.GetDexFile().DecodeDebugLocalInfo(
-        //       disk_code_item, is_static, cdii.GetMemberIndex(), GetLocalsCb, debug_info);
-        header.GetDexFile().DecodeDebugPositionInfo(disk_code_item, GetPositionsCb, debug_info);
-      }
       class_data_.VirtualMethods().push_back(
-          std::unique_ptr<MethodItem>(new MethodItem(access_flags, method_item, code_item)));
+          std::unique_ptr<MethodItem>(GenerateMethodItem(header, cdii)));
     }
   }
 }
 
+MethodItem* ClassDef::GenerateMethodItem(Header& header, ClassDataItemIterator& cdii) {
+  MethodId* method_item = header.MethodIds()[cdii.GetMemberIndex()].get();
+  uint32_t access_flags = cdii.GetRawMemberAccessFlags();
+  const DexFile::CodeItem* disk_code_item = cdii.GetMethodCodeItem();
+  CodeItem* code_item = nullptr;
+  DebugInfoItem* debug_info = nullptr;
+  if (disk_code_item != nullptr) {
+    code_item = new CodeItem(*disk_code_item, header);
+    code_item->SetOffset(cdii.GetMethodCodeItemOffset());
+    debug_info = code_item->DebugInfo();
+  }
+  if (debug_info != nullptr) {
+    bool is_static = (access_flags & kAccStatic) != 0;
+    header.GetDexFile().DecodeDebugLocalInfo(
+        disk_code_item, is_static, cdii.GetMemberIndex(), GetLocalsCb, debug_info);
+    header.GetDexFile().DecodeDebugPositionInfo(disk_code_item, GetPositionsCb, debug_info);
+  }
+  return new MethodItem(access_flags, method_item, code_item);
+}
+
 CodeItem::CodeItem(const DexFile::CodeItem& disk_code_item, Header& header) {
   registers_size_ = disk_code_item.registers_size_;
   ins_size_ = disk_code_item.ins_size_;
diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h
index bef5c0d..fcd3ab0 100644
--- a/dexlayout/dex_ir.h
+++ b/dexlayout/dex_ir.h
@@ -167,6 +167,14 @@
   uint32_t MethodIdsSize() const { return method_ids_.Size(); }
   uint32_t ClassDefsSize() const { return class_defs_.Size(); }
 
+  TypeId* GetTypeIdOrNullPtr(uint16_t index) {
+    return index == DexFile::kDexNoIndex16 ? nullptr : TypeIds()[index].get();
+  }
+
+  StringId* GetStringIdOrNullPtr(uint32_t index) {
+    return index == DexFile::kDexNoIndex ? nullptr : StringIds()[index].get();
+  }
+
   void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
 
  private:
@@ -432,6 +440,8 @@
   std::vector<std::unique_ptr<ArrayItem>>* StaticValues() { return static_values_; }
   ClassData* GetClassData() { return &class_data_; }
 
+  MethodItem* GenerateMethodItem(Header& header, ClassDataItemIterator& cdii);
+
   void Accept(AbstractDispatcher* dispatch) { dispatch->Dispatch(this); }
 
  private:
@@ -497,8 +507,7 @@
     insn_count_ = disk_try_item.insn_count_;
     for (CatchHandlerIterator it(disk_code_item, disk_try_item); it.HasNext(); it.Next()) {
       const uint16_t type_index = it.GetHandlerTypeIndex();
-      const TypeId* type_id = type_index != DexFile::kDexNoIndex16 ?
-          header.TypeIds()[type_index].get() : nullptr;
+      const TypeId* type_id = header.GetTypeIdOrNullPtr(type_index);
       handlers_.push_back(std::unique_ptr<const CatchHandler>(
           new CatchHandler(type_id, it.GetHandlerAddress())));
     }
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index a774921..0b31614 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -63,6 +63,22 @@
 }
 
 /*
+ * Converts a type descriptor to human-readable "dotted" form.  For
+ * example, "Ljava/lang/String;" becomes "java.lang.String", and
+ * "[I" becomes "int[]".  Also converts '$' to '.', which means this
+ * form can't be converted back to a descriptor.
+ */
+static std::string DescriptorToDotWrapper(const char* descriptor) {
+  std::string result = DescriptorToDot(descriptor);
+  size_t found = result.find('$');
+  while (found != std::string::npos) {
+    result[found] = '.';
+    found = result.find('$', found);
+  }
+  return result;
+}
+
+/*
  * Converts the class name portion of a type descriptor to human-readable
  * "dotted" form. For example, "Ljava/lang/String;" becomes "String".
  */
@@ -469,7 +485,9 @@
   fprintf(out_file_, "class_idx           : %d\n", class_def->ClassType()->GetOffset());
   fprintf(out_file_, "access_flags        : %d (0x%04x)\n",
           class_def->GetAccessFlags(), class_def->GetAccessFlags());
-  fprintf(out_file_, "superclass_idx      : %d\n", class_def->Superclass()->GetOffset());
+  uint32_t superclass_idx =  class_def->Superclass() == nullptr ?
+      DexFile::kDexNoIndex16 : class_def->Superclass()->GetOffset();
+  fprintf(out_file_, "superclass_idx      : %d\n", superclass_idx);
   fprintf(out_file_, "interfaces_off      : %d (0x%06x)\n",
           class_def->InterfacesOffset(), class_def->InterfacesOffset());
   uint32_t source_file_offset = 0xffffffffU;
@@ -599,7 +617,7 @@
   if (options_.output_format_ == kOutputPlain) {
     fprintf(out_file_, "    #%d              : '%s'\n", i, interface_name);
   } else {
-    std::string dot(DescriptorToDot(interface_name));
+    std::string dot(DescriptorToDotWrapper(interface_name));
     fprintf(out_file_, "<implements name=\"%s\">\n</implements>\n", dot.c_str());
   }
 }
@@ -1001,7 +1019,7 @@
   const char* back_descriptor = method_id->Class()->GetStringId()->Data();
 
   // Generate header.
-  std::string dot(DescriptorToDot(back_descriptor));
+  std::string dot(DescriptorToDotWrapper(back_descriptor));
   fprintf(out_file_, "%06x:                                        |[%06x] %s.%s:%s\n",
           code_offset, code_offset, dot.c_str(), name, type_descriptor);
 
@@ -1082,7 +1100,7 @@
     if (constructor) {
       std::string dot(DescriptorClassToDot(back_descriptor));
       fprintf(out_file_, "<constructor name=\"%s\"\n", dot.c_str());
-      dot = DescriptorToDot(back_descriptor);
+      dot = DescriptorToDotWrapper(back_descriptor);
       fprintf(out_file_, " type=\"%s\"\n", dot.c_str());
     } else {
       fprintf(out_file_, "<method name=\"%s\"\n", name);
@@ -1091,7 +1109,7 @@
         fprintf(stderr, "bad method type descriptor '%s'\n", type_descriptor);
         goto bail;
       }
-      std::string dot(DescriptorToDot(return_type + 1));
+      std::string dot(DescriptorToDotWrapper(return_type + 1));
       fprintf(out_file_, " return=\"%s\"\n", dot.c_str());
       fprintf(out_file_, " abstract=%s\n", QuotedBool((flags & kAccAbstract) != 0));
       fprintf(out_file_, " native=%s\n", QuotedBool((flags & kAccNative) != 0));
@@ -1133,7 +1151,7 @@
       }
       // Null terminate and display.
       *cp++ = '\0';
-      std::string dot(DescriptorToDot(tmp_buf));
+      std::string dot(DescriptorToDotWrapper(tmp_buf));
       fprintf(out_file_, "<parameter name=\"arg%d\" type=\"%s\">\n"
                         "</parameter>\n", arg_num++, dot.c_str());
     }  // while
@@ -1178,7 +1196,7 @@
     }
   } else if (options_.output_format_ == kOutputXml) {
     fprintf(out_file_, "<field name=\"%s\"\n", name);
-    std::string dot(DescriptorToDot(type_descriptor));
+    std::string dot(DescriptorToDotWrapper(type_descriptor));
     fprintf(out_file_, " type=\"%s\"\n", dot.c_str());
     fprintf(out_file_, " transient=%s\n", QuotedBool((flags & kAccTransient) != 0));
     fprintf(out_file_, " volatile=%s\n", QuotedBool((flags & kAccVolatile) != 0));
@@ -1335,7 +1353,7 @@
     std::string dot(DescriptorClassToDot(class_descriptor));
     fprintf(out_file_, "<class name=\"%s\"\n", dot.c_str());
     if (superclass_descriptor != nullptr) {
-      dot = DescriptorToDot(superclass_descriptor);
+      dot = DescriptorToDotWrapper(superclass_descriptor);
       fprintf(out_file_, " extends=\"%s\"\n", dot.c_str());
     }
     fprintf(out_file_, " interface=%s\n",
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
new file mode 100644
index 0000000..1757548
--- /dev/null
+++ b/dexlayout/dexlayout_test.cc
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/stringprintf.h"
+#include "common_runtime_test.h"
+#include "utils.h"
+
+namespace art {
+
+class DexLayoutTest : public CommonRuntimeTest {
+ 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) {
+    // 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;
+    }
+    return true;
+  }
+
+  std::string dex_file_;
+};
+
+
+TEST_F(DexLayoutTest, FullPlainOutput) {
+  std::string error_msg;
+  ASSERT_TRUE(Exec(&error_msg)) << error_msg;
+}
+
+}  // namespace art