Leverage field access info for determining if field is only written in clinit

Change-Id: Ica16c5d5fd2b480403c170bf1978b0fc69ee2ca5
diff --git a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
index 6308a16..f8aaa42 100644
--- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -19,7 +19,7 @@
 import java.util.List;
 import java.util.Map;
 
-public class DirectMappedDexApplication extends DexApplication {
+public class DirectMappedDexApplication extends DexApplication implements DexDefinitionSupplier {
 
   // Mapping from code objects to their encoded-method owner. Used for asserting unique ownership
   // and debugging purposes.
@@ -70,12 +70,46 @@
   }
 
   @Override
+  public DexDefinition definitionFor(DexReference reference) {
+    if (reference.isDexType()) {
+      return definitionFor(reference.asDexType());
+    }
+    if (reference.isDexMethod()) {
+      return definitionFor(reference.asDexMethod());
+    }
+    assert reference.isDexField();
+    return definitionFor(reference.asDexField());
+  }
+
+  @Override
+  public DexEncodedField definitionFor(DexField field) {
+    DexClass clazz = definitionFor(field.holder);
+    return clazz != null ? clazz.lookupField(field) : null;
+  }
+
+  @Override
+  public DexEncodedMethod definitionFor(DexMethod method) {
+    DexClass clazz = definitionFor(method.holder);
+    return clazz != null ? clazz.lookupMethod(method) : null;
+  }
+
+  @Override
   public DexClass definitionFor(DexType type) {
     assert type.isClassType() : "Cannot lookup definition for type: " + type;
     return allClasses.get(type);
   }
 
   @Override
+  public DexProgramClass definitionForProgramType(DexType type) {
+    return programDefinitionFor(type);
+  }
+
+  @Override
+  public DexItemFactory dexItemFactory() {
+    return dexItemFactory;
+  }
+
+  @Override
   public DexProgramClass programDefinitionFor(DexType type) {
     DexClass clazz = definitionFor(type);
     return clazz instanceof DexProgramClass ? clazz.asProgramClass() : null;
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
index 1478f2d..3a307cf 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
@@ -36,11 +36,13 @@
     infos.entrySet().removeIf(entry -> predicate.test(entry.getKey(), entry.getValue()));
   }
 
-  public FieldAccessInfoCollectionImpl rewrittenWithLens(GraphLense lens) {
+  public FieldAccessInfoCollectionImpl rewrittenWithLens(
+      DexDefinitionSupplier definitions, GraphLense lens) {
     FieldAccessInfoCollectionImpl collection = new FieldAccessInfoCollectionImpl();
     infos.forEach(
         (field, info) ->
-            collection.infos.put(lens.lookupField(field), info.rewrittenWithLens(lens)));
+            collection.infos.put(
+                lens.lookupField(field), info.rewrittenWithLens(definitions, lens)));
     return collection;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
index fcd4f5c..a263cb6 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
@@ -189,25 +189,31 @@
     writesWithContexts = null;
   }
 
-  public FieldAccessInfoImpl rewrittenWithLens(GraphLense lens) {
+  public FieldAccessInfoImpl rewrittenWithLens(DexDefinitionSupplier definitions, GraphLense lens) {
     FieldAccessInfoImpl rewritten = new FieldAccessInfoImpl(lens.lookupField(field));
     if (readsWithContexts != null) {
       rewritten.readsWithContexts = new IdentityHashMap<>();
       readsWithContexts.forEach(
-          (access, contexts) ->
-              rewritten
-                  .readsWithContexts
-                  .computeIfAbsent(lens.lookupField(access), ignore -> Sets.newIdentityHashSet())
-                  .addAll(contexts));
+          (access, contexts) -> {
+            Set<DexEncodedMethod> newContexts =
+                rewritten.readsWithContexts.computeIfAbsent(
+                    lens.lookupField(access), ignore -> Sets.newIdentityHashSet());
+            for (DexEncodedMethod context : contexts) {
+              newContexts.add(lens.mapDexEncodedMethod(context, definitions));
+            }
+          });
     }
     if (writesWithContexts != null) {
       rewritten.writesWithContexts = new IdentityHashMap<>();
       writesWithContexts.forEach(
-          (access, contexts) ->
-              rewritten
-                  .writesWithContexts
-                  .computeIfAbsent(lens.lookupField(access), ignore -> Sets.newIdentityHashSet())
-                  .addAll(contexts));
+          (access, contexts) -> {
+            Set<DexEncodedMethod> newContexts =
+                rewritten.writesWithContexts.computeIfAbsent(
+                    lens.lookupField(access), ignore -> Sets.newIdentityHashSet());
+            for (DexEncodedMethod context : contexts) {
+              newContexts.add(lens.mapDexEncodedMethod(context, definitions));
+            }
+          });
     }
     return rewritten;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 16dab7e..8c5dd71 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -420,6 +420,10 @@
 
   public DexEncodedMethod mapDexEncodedMethod(
       DexEncodedMethod originalEncodedMethod, DexDefinitionSupplier definitions) {
+    assert originalEncodedMethod != DexEncodedMethod.SENTINEL;
+    if (originalEncodedMethod == DexEncodedMethod.ANNOTATION_REFERENCE) {
+      return DexEncodedMethod.ANNOTATION_REFERENCE;
+    }
     DexMethod newMethod = getRenamedMethodSignature(originalEncodedMethod.method);
     // Note that:
     // * Even if `newMethod` is the same as `originalEncodedMethod.method`, we still need to look it
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index f89e220..80ca84e 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -468,7 +468,8 @@
     this.virtualMethodsTargetedByInvokeDirect =
         lense.rewriteMethodsConservatively(previous.virtualMethodsTargetedByInvokeDirect);
     this.liveMethods = lense.rewriteMethodsConservatively(previous.liveMethods);
-    this.fieldAccessInfoCollection = previous.fieldAccessInfoCollection.rewrittenWithLens(lense);
+    this.fieldAccessInfoCollection =
+        previous.fieldAccessInfoCollection.rewrittenWithLens(application, lense);
     this.instanceFieldsWrittenOnlyInEnclosingInstanceInitializers =
         rewriteItems(
             previous.instanceFieldsWrittenOnlyInEnclosingInstanceInitializers, lense::lookupField);
@@ -791,10 +792,28 @@
     return instanceFieldsWrittenOnlyInEnclosingInstanceInitializers.contains(field.field);
   }
 
+  public boolean isStaticFieldWrittenOnlyInEnclosingStaticInitializerNew(DexEncodedField field) {
+    assert checkIfObsolete();
+    assert isFieldWritten(field) : "Expected field `" + field.toSourceString() + "` to be written";
+    if (!isPinned(field.field)) {
+      DexEncodedMethod staticInitializer =
+          definitionFor(field.field.holder).asProgramClass().getClassInitializer();
+      if (staticInitializer != null) {
+        FieldAccessInfo fieldAccessInfo = fieldAccessInfoCollection.get(field.field);
+        return fieldAccessInfo != null
+            && fieldAccessInfo.isWritten()
+            && !fieldAccessInfo.isWrittenOutside(staticInitializer);
+      }
+    }
+    return false;
+  }
+
   public boolean isStaticFieldWrittenOnlyInEnclosingStaticInitializer(DexEncodedField field) {
     assert checkIfObsolete();
     assert isFieldWritten(field) : "Expected field `" + field.toSourceString() + "` to be written";
-    return staticFieldsWrittenOnlyInEnclosingStaticInitializer.contains(field.field);
+    boolean result = staticFieldsWrittenOnlyInEnclosingStaticInitializer.contains(field.field);
+    assert result == isStaticFieldWrittenOnlyInEnclosingStaticInitializerNew(field);
+    return result;
   }
 
   public boolean mayPropagateValueFor(DexReference reference) {