Add header offset and string data flag to Layout

Also adds some toString debugging info for Layout.

Bug: b/249922554
Change-Id: I4458926ffb165d2436337de9117b8dd70e4af6dc
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 0a679e8..55c103a 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -243,7 +243,7 @@
     layout.setEndOfFile(dest.position());
 
     // Now that we have all mixedSectionOffsets, lets write the indexed items.
-    dest.moveTo(Constants.TYPE_HEADER_ITEM_SIZE);
+    dest.moveTo(layout.headerOffset + Constants.TYPE_HEADER_ITEM_SIZE);
     writeFixedSectionItems(mapping.getStrings(), layout.stringIdsOffset, this::writeStringItem);
     writeFixedSectionItems(mapping.getTypes(), layout.typeIdsOffset, this::writeTypeItem);
     writeFixedSectionItems(mapping.getProtos(), layout.protoIdsOffset, this::writeProtoItem);
@@ -794,12 +794,12 @@
   }
 
   private void writeHeader(Layout layout) {
-    dest.moveTo(0);
+    dest.moveTo(layout.headerOffset);
     dest.putBytes(Constants.DEX_FILE_MAGIC_PREFIX);
     dest.putBytes(dexVersionBytes());
     dest.putByte(Constants.DEX_FILE_MAGIC_SUFFIX);
     // Leave out checksum and signature for now.
-    dest.moveTo(Constants.FILE_SIZE_OFFSET);
+    dest.moveTo(layout.headerOffset + Constants.FILE_SIZE_OFFSET);
     dest.putInt(layout.getEndOfFile());
     dest.putInt(Constants.TYPE_HEADER_ITEM_SIZE);
     dest.putInt(Constants.ENDIAN_CONSTANT);
@@ -832,9 +832,11 @@
   private void writeSignature(Layout layout) {
     try {
       MessageDigest md = MessageDigest.getInstance("SHA-1");
-      md.update(dest.asArray(), Constants.FILE_SIZE_OFFSET,
-          layout.getEndOfFile() - Constants.FILE_SIZE_OFFSET);
-      md.digest(dest.asArray(), Constants.SIGNATURE_OFFSET, 20);
+      md.update(
+          dest.asArray(),
+          layout.headerOffset + Constants.FILE_SIZE_OFFSET,
+          layout.getEndOfFile() - layout.headerOffset - Constants.FILE_SIZE_OFFSET);
+      md.digest(dest.asArray(), layout.headerOffset + Constants.SIGNATURE_OFFSET, 20);
     } catch (Exception e) {
       throw new RuntimeException(e);
     }
@@ -842,9 +844,11 @@
 
   private void writeChecksum(Layout layout) {
     Adler32 adler = new Adler32();
-    adler.update(dest.asArray(), Constants.SIGNATURE_OFFSET,
-        layout.getEndOfFile() - Constants.SIGNATURE_OFFSET);
-    dest.moveTo(Constants.CHECKSUM_OFFSET);
+    adler.update(
+        dest.asArray(),
+        layout.headerOffset + Constants.SIGNATURE_OFFSET,
+        layout.getEndOfFile() - layout.headerOffset - Constants.SIGNATURE_OFFSET);
+    dest.moveTo(layout.headerOffset + Constants.CHECKSUM_OFFSET);
     dest.putInt((int) adler.getValue());
   }
 
@@ -858,6 +862,7 @@
     private static final int NOT_SET = -1;
 
     // Fixed size constant pool sections
+    final int headerOffset;
     final int stringIdsOffset;
     final int typeIdsOffset;
     final int protoIdsOffset;
@@ -884,6 +889,7 @@
     private int endOfFile = NOT_SET;
 
     private Layout(
+        int headerOffset,
         int stringIdsOffset,
         int typeIdsOffset,
         int protoIdsOffset,
@@ -893,6 +899,7 @@
         int callSiteIdsOffset,
         int methodHandleIdsOffset,
         int dataSectionOffset) {
+      this.headerOffset = headerOffset;
       this.stringIdsOffset = stringIdsOffset;
       this.typeIdsOffset = typeIdsOffset;
       this.protoIdsOffset = protoIdsOffset;
@@ -913,10 +920,17 @@
     }
 
     static Layout from(ObjectToOffsetMapping mapping) {
-      int offset = 0;
+      return from(mapping, 0, true);
+    }
+
+    static Layout from(ObjectToOffsetMapping mapping, int offset, boolean includeStringData) {
       return new Layout(
-          offset = Constants.TYPE_HEADER_ITEM_SIZE,
-          offset += mapping.getStrings().size() * Constants.TYPE_STRING_ID_ITEM_SIZE,
+          offset,
+          offset += Constants.TYPE_HEADER_ITEM_SIZE,
+          offset +=
+              includeStringData
+                  ? mapping.getStrings().size() * Constants.TYPE_STRING_ID_ITEM_SIZE
+                  : 0,
           offset += mapping.getTypes().size() * Constants.TYPE_TYPE_ID_ITEM_SIZE,
           offset += mapping.getProtos().size() * Constants.TYPE_PROTO_ID_ITEM_SIZE,
           offset += mapping.getFields().size() * Constants.TYPE_FIELD_ID_ITEM_SIZE,
@@ -1055,6 +1069,95 @@
     public void setEndOfFile(int endOfFile) {
       this.endOfFile = endOfFile;
     }
+
+    @Override
+    public String toString() {
+      StringBuilder builder = new StringBuilder();
+      if (false) {
+        builder.append("headerOffset: ").append(headerOffset).append("\n");
+        builder.append("stringIdsOffset: ").append(stringIdsOffset).append("\n");
+        builder.append("typeIdsOffset: ").append(typeIdsOffset).append("\n");
+        builder.append("protoIdsOffset: ").append(protoIdsOffset).append("\n");
+        builder.append("fieldIdsOffset: ").append(fieldIdsOffset).append("\n");
+        builder.append("methodIdsOffset: ").append(methodIdsOffset).append("\n");
+        builder.append("classDefsOffset: ").append(classDefsOffset).append("\n");
+        builder.append("callSiteIdsOffset: ").append(callSiteIdsOffset).append("\n");
+        builder.append("methodHandleIdsOffset: ").append(methodHandleIdsOffset).append("\n");
+        builder.append("dataSectionOffset: ").append(dataSectionOffset).append("\n");
+
+        // Mixed size sections
+        builder.append("codesOffset: ").append(codesOffset).append("\n");
+        builder.append("debugInfosOffset: ").append(debugInfosOffset).append("\n");
+
+        builder.append("typeListsOffset: ").append(typeListsOffset).append("\n");
+        builder.append("stringDataOffsets: ").append(stringDataOffsets).append("\n");
+        builder.append("annotationsOffset: ").append(annotationsOffset).append("\n");
+        builder.append("annotationSetsOffset: ").append(annotationSetsOffset).append("\n");
+        builder
+            .append("annotationSetRefListsOffset: ")
+            .append(annotationSetRefListsOffset)
+            .append("\n");
+        builder
+            .append("annotationDirectoriesOffset: ")
+            .append(annotationDirectoriesOffset)
+            .append("\n");
+        builder.append("classDataOffset: ").append(classDataOffset).append("\n");
+        builder.append("encodedArraysOffset: ").append(encodedArraysOffset).append("\n");
+        builder.append("mapOffset: ").append(mapOffset).append("\n");
+        builder.append("endOfFile: ").append(endOfFile).append("\n");
+      } else {
+        builder.append("Header: ").append(stringIdsOffset - headerOffset).append("\n");
+        builder.append("StringIds: ").append(typeIdsOffset - stringIdsOffset).append("\n");
+        builder.append("typeIds: ").append(protoIdsOffset - typeIdsOffset).append("\n");
+        builder.append("protoIds: ").append(fieldIdsOffset - protoIdsOffset).append("\n");
+        builder.append("fieldIds: ").append(methodIdsOffset - fieldIdsOffset).append("\n");
+        builder.append("methodIds: ").append(classDefsOffset - methodIdsOffset).append("\n");
+        builder.append("classDefs: ").append(callSiteIdsOffset - classDefsOffset).append("\n");
+        builder
+            .append("callSiteIds: ")
+            .append(methodHandleIdsOffset - callSiteIdsOffset)
+            .append("\n");
+        builder
+            .append("methodHandleIds: ")
+            .append(dataSectionOffset - methodHandleIdsOffset)
+            .append("\n");
+
+        // Mixed size sections
+        builder.append("code: ").append(debugInfosOffset - codesOffset).append("\n");
+        builder.append("debugInfo: ").append(typeListsOffset - debugInfosOffset).append("\n");
+
+        builder
+            .append("typeList: ")
+            .append(
+                (stringDataOffsets > 0 ? stringDataOffsets : annotationsOffset) - typeListsOffset)
+            .append("\n");
+        builder
+            .append("stringData: ")
+            .append(stringDataOffsets > 0 ? annotationsOffset - stringDataOffsets : 0)
+            .append("\n");
+        builder.append("annotations: ").append(classDataOffset - annotationsOffset).append("\n");
+        builder.append("classData: ").append(encodedArraysOffset - classDataOffset).append("\n");
+        builder
+            .append("encodedArrays: ")
+            .append(mapOffset - annotationSetRefListsOffset)
+            .append("\n");
+        builder
+            .append("annotationSets: ")
+            .append(annotationSetRefListsOffset - annotationSetsOffset)
+            .append("\n");
+        builder
+            .append("annotationSetRefLists: ")
+            .append(annotationDirectoriesOffset - annotationSetRefListsOffset)
+            .append("\n");
+        builder
+            .append("annotationDirectories: ")
+            .append(mapOffset - annotationDirectoriesOffset)
+            .append("\n");
+        builder.append("map: ").append(endOfFile - mapOffset).append("\n");
+        builder.append("endOfFile: ").append(endOfFile).append("\n");
+      }
+      return builder.toString();
+    }
   }
 
   /**