Transform static values eagerly rather than on use.

Apparently there is some situations where a single static value
may be referenced by multiple sites. Not transforming them eagerly
lead to oversized dex files (and could crash the merge).

Change-Id: I4ac5b9cd621b0fff1e5ba247c9590aa0d562cd65
diff --git a/dx/src/com/android/dx/io/DexBuffer.java b/dx/src/com/android/dx/io/DexBuffer.java
index d10b08c..217de72 100644
--- a/dx/src/com/android/dx/io/DexBuffer.java
+++ b/dx/src/com/android/dx/io/DexBuffer.java
@@ -217,7 +217,7 @@
         return length;
     }
 
-    private static int fourByteAlign(int position) {
+    public static int fourByteAlign(int position) {
         return (position + 3) & ~3;
     }
 
diff --git a/dx/src/com/android/dx/merge/DexMerger.java b/dx/src/com/android/dx/merge/DexMerger.java
index b807117..1ba876a 100644
--- a/dx/src/com/android/dx/merge/DexMerger.java
+++ b/dx/src/com/android/dx/merge/DexMerger.java
@@ -33,7 +33,9 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Combine two dex files into one.
@@ -595,6 +597,8 @@
         transformAnnotationSets(dexB, bIndexMap);
         transformAnnotationDirectories(dexA, aIndexMap);
         transformAnnotationDirectories(dexB, bIndexMap);
+        transformStaticValues(dexA, aIndexMap);
+        transformStaticValues(dexB, bIndexMap);
     }
 
     private void transformAnnotationSets(DexBuffer in, IndexMap indexMap) {
@@ -617,6 +621,16 @@
         }
     }
 
+    private void transformStaticValues(DexBuffer in, IndexMap indexMap) {
+        TableOfContents.Section section = in.getTableOfContents().encodedArrays;
+        if (section.exists()) {
+            DexBuffer.Section staticValuesIn = in.open(section.off);
+            for (int i = 0; i < section.size; i++) {
+                transformStaticValues(staticValuesIn, indexMap);
+            }
+        }
+    }
+
     /**
      * Reads a class_def_item beginning at {@code in} and writes the index and
      * data.
@@ -644,13 +658,7 @@
         }
 
         int staticValuesOff = classDef.getStaticValuesOffset();
-        if (staticValuesOff == 0) {
-            idsDefsOut.writeInt(0);
-        } else {
-            DexBuffer.Section staticValuesIn = in.open(staticValuesOff);
-            idsDefsOut.writeInt(encodedArrayOut.getPosition());
-            transformStaticValues(staticValuesIn, indexMap);
-        }
+        idsDefsOut.writeInt(indexMap.adjustStaticValues(staticValuesOff));
     }
 
     /**
@@ -839,6 +847,7 @@
 
     private void transformStaticValues(DexBuffer.Section in, IndexMap indexMap) {
         contentsOut.encodedArrays.size++;
+        indexMap.putStaticValuesOffset(in.getPosition(), encodedArrayOut.getPosition());
         indexMap.adjustEncodedArray(in.readEncodedArray()).writeTo(encodedArrayOut);
     }
 
@@ -914,6 +923,9 @@
                 // at most 1/3 of the bytes in an encoding arrays section are uleb/sleb
                 annotation += (int) Math.ceil(contents.annotations.byteCount * 1.34);
             }
+
+            typeList = DexBuffer.fourByteAlign(typeList);
+            code = DexBuffer.fourByteAlign(code);
         }
 
         public void minusWaste(DexMerger dexMerger) {
diff --git a/dx/src/com/android/dx/merge/IndexMap.java b/dx/src/com/android/dx/merge/IndexMap.java
index a7b20be..a5c9584 100644
--- a/dx/src/com/android/dx/merge/IndexMap.java
+++ b/dx/src/com/android/dx/merge/IndexMap.java
@@ -48,6 +48,7 @@
     private final HashMap<Integer, Integer> annotationOffsets;
     private final HashMap<Integer, Integer> annotationSetOffsets;
     private final HashMap<Integer, Integer> annotationDirectoryOffsets;
+    private final HashMap<Integer, Integer> staticValuesOffsets;
 
     public IndexMap(DexBuffer target, TableOfContents tableOfContents) {
         this.target = target;
@@ -60,14 +61,16 @@
         this.annotationOffsets = new HashMap<Integer, Integer>();
         this.annotationSetOffsets = new HashMap<Integer, Integer>();
         this.annotationDirectoryOffsets = new HashMap<Integer, Integer>();
+        this.staticValuesOffsets = new HashMap<Integer, Integer>();
 
         /*
-         * A type list, annotation set, or annotation directory at offset 0 is
-         * always empty. Always map offset 0 to 0.
+         * A type list, annotation set, annotation directory, or static value at
+         * offset 0 is always empty. Always map offset 0 to 0.
          */
         this.typeListOffsets.put(0, 0);
         this.annotationSetOffsets.put(0, 0);
         this.annotationDirectoryOffsets.put(0, 0);
+        this.staticValuesOffsets.put(0, 0);
     }
 
     public void putTypeListOffset(int oldOffset, int newOffset) {
@@ -98,6 +101,13 @@
         annotationDirectoryOffsets.put(oldOffset, newOffset);
     }
 
+    public void putStaticValuesOffset(int oldOffset, int newOffset) {
+        if (oldOffset <= 0 || newOffset <= 0) {
+            throw new IllegalArgumentException();
+        }
+        staticValuesOffsets.put(oldOffset, newOffset);
+    }
+
     public int adjustString(int stringIndex) {
         return stringIndex == ClassDef.NO_INDEX ? ClassDef.NO_INDEX : stringIds[stringIndex];
     }
@@ -145,6 +155,10 @@
         return annotationDirectoryOffsets.get(annotationDirectoryOffset);
     }
 
+    public int adjustStaticValues(int staticValuesOffset) {
+        return staticValuesOffsets.get(staticValuesOffset);
+    }
+
     public MethodId adjust(MethodId methodId) {
         return new MethodId(target,
                 adjustType(methodId.getDeclaringClassIndex()),