Merge "Add support for AutoValue in examples."
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 da3722f..b1db651 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -58,7 +58,6 @@
 import it.unimi.dsi.fastutil.objects.Reference2IntLinkedOpenHashMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
 import java.security.MessageDigest;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -69,7 +68,6 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.function.Consumer;
-import java.util.function.Function;
 import java.util.function.ToIntFunction;
 import java.util.zip.Adler32;
 
@@ -101,29 +99,13 @@
     }
     dest.putUleb128(mapping.getOffsetFor(annotation.type));
     dest.putUleb128(annotation.elements.length);
-    assert isSorted(annotation.elements, (element) -> element.name);
+    assert PresortedComparable.isSorted(annotation.elements, (element) -> element.name);
     for (DexAnnotationElement element : annotation.elements) {
       dest.putUleb128(mapping.getOffsetFor(element.name));
       element.value.writeTo(dest, mapping);
     }
   }
 
-  private static <T extends PresortedComparable<T>> boolean isSorted(KeyedDexItem<T>[] items) {
-    return isSorted(items, KeyedDexItem::getKey);
-  }
-
-  private static <S, T extends Comparable<T>> boolean isSorted(S[] items, Function<S, T> getter) {
-    T current = null;
-    for (S item : items) {
-      T next = getter.apply(item);
-      if (current != null && current.compareTo(next) >= 0) {
-        return false;
-      }
-      current = next;
-    }
-    return true;
-  }
-
   public FileWriter collect() {
     // Use the class array from the mapping, as it has a deterministic iteration order.
     new ProgramClassDependencyCollector(application, mapping.getClasses())
@@ -518,7 +500,7 @@
 
   private void writeAnnotationSet(DexAnnotationSet set) {
     assert !set.isEmpty();
-    assert isSorted(set.annotations, (item) -> item.annotation.type);
+    assert PresortedComparable.isSorted(set.annotations, (item) -> item.annotation.type);
     mixedSectionOffsets.setOffsetFor(set, dest.align(4));
     if (Log.ENABLED) {
       Log.verbose(getClass(), "Writing AnnotationSet @ 0x%08x.", dest.position());
@@ -564,7 +546,7 @@
   }
 
   private void writeEncodedFields(DexEncodedField[] fields) {
-    assert isSorted(fields);
+    assert PresortedComparable.isSorted(fields);
     int currentOffset = 0;
     for (DexEncodedField field : fields) {
       int nextOffset = mapping.getOffsetFor(field.field);
@@ -576,7 +558,7 @@
   }
 
   private void writeEncodedMethods(DexEncodedMethod[] methods, boolean clearBodies) {
-    assert isSorted(methods);
+    assert PresortedComparable.isSorted(methods);
     int currentOffset = 0;
     for (DexEncodedMethod method : methods) {
       int nextOffset = mapping.getOffsetFor(method.method);
@@ -614,21 +596,12 @@
   }
 
   private void addStaticFieldValues(DexProgramClass clazz) {
-    DexEncodedField[] fields = clazz.staticFields();
-    int length = 0;
-    List<DexValue> values = new ArrayList<>(fields.length);
-    for (int i = 0; i < fields.length; i++) {
-      DexEncodedField field = fields[i];
-      assert field.staticValue != null;
-      values.add(field.staticValue);
-      if (!field.staticValue.isDefault(field.field.type, application.dexItemFactory)) {
-        length = i + 1;
-      }
-    }
-    if (length > 0) {
-      DexEncodedArray staticValues = new DexEncodedArray(
-          values.subList(0, length).toArray(new DexValue[length]));
-      clazz.setStaticValues(staticValues);
+    clazz.computeStaticValues(application.dexItemFactory);
+    // We have collected the individual components of this array due to the data stored in
+    // DexEncodedField#staticValues. However, we have to collect the DexEncodedArray itself
+    // here.
+    DexEncodedArray staticValues = clazz.getStaticValues();
+    if (staticValues != null) {
       mixedSectionOffsets.add(staticValues);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index fcb4684..b64053a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -8,17 +8,22 @@
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.utils.ProgramResource;
 import com.android.tools.r8.utils.ProgramResource.Kind;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 import java.util.function.Supplier;
 
 public class DexProgramClass extends DexClass implements Supplier<DexProgramClass> {
+
+  private static DexEncodedArray SENTINEL_NOT_YET_COMPUTED = new DexEncodedArray(new DexValue[0]);
+
   private final ProgramResource.Kind originKind;
-  private DexEncodedArray staticValues;
+  private DexEncodedArray staticValues = SENTINEL_NOT_YET_COMPUTED;
   private final Collection<DexProgramClass> synthesizedFrom;
 
   public DexProgramClass(
@@ -214,11 +219,38 @@
     return accumulated;
   }
 
-  public void setStaticValues(DexEncodedArray staticValues) {
-    this.staticValues = staticValues;
+  public void computeStaticValues(DexItemFactory factory) {
+    // It does not actually hurt to compute this multiple times. So racing on staticValues is OK.
+    if (staticValues == SENTINEL_NOT_YET_COMPUTED) {
+      synchronized (staticFields) {
+        assert PresortedComparable.isSorted(staticFields);
+        DexEncodedField[] fields = staticFields;
+        int length = 0;
+        List<DexValue> values = new ArrayList<>(fields.length);
+        for (int i = 0; i < fields.length; i++) {
+          DexEncodedField field = fields[i];
+          assert field.staticValue != null;
+          values.add(field.staticValue);
+          if (!field.staticValue.isDefault(field.field.type, factory)) {
+            length = i + 1;
+          }
+        }
+        if (length > 0) {
+          staticValues = new DexEncodedArray(
+              values.subList(0, length).toArray(new DexValue[length]));
+        } else {
+          staticValues = null;
+        }
+      }
+    }
   }
 
   public DexEncodedArray getStaticValues() {
+    // The sentinel value is left over for classes that actually have no fields.
+    if (staticValues == SENTINEL_NOT_YET_COMPUTED) {
+      assert !hasMethodsOrFields();
+      return null;
+    }
     return staticValues;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/PresortedComparable.java b/src/main/java/com/android/tools/r8/graph/PresortedComparable.java
index eb130d6..a4ee2ee 100644
--- a/src/main/java/com/android/tools/r8/graph/PresortedComparable.java
+++ b/src/main/java/com/android/tools/r8/graph/PresortedComparable.java
@@ -4,8 +4,26 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.naming.NamingLens;
+import java.util.function.Function;
 
 public interface PresortedComparable<T> extends Presorted, Comparable<T> {
+
+  static <T extends PresortedComparable<T>> boolean isSorted(KeyedDexItem<T>[] items) {
+    return isSorted(items, KeyedDexItem::getKey);
+  }
+
+  static <S, T extends Comparable<T>> boolean isSorted(S[] items, Function<S, T> getter) {
+    T current = null;
+    for (S item : items) {
+      T next = getter.apply(item);
+      if (current != null && current.compareTo(next) >= 0) {
+        return false;
+      }
+      current = next;
+    }
+    return true;
+  }
+
   // Slow comparison methods that make no use of indices for comparisons. These are used
   // for sorting operations when reading dex files.
   int slowCompareTo(T other);