Record method handle accesses in field access info

Change-Id: Id6e0c08226f0b71aefecd63fe2873d3bc729c68e
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
index 2bd8acf..38d3a8e 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfo.java
@@ -32,12 +32,20 @@
 
   boolean hasReflectiveAccess();
 
+  default boolean isAccessedFromMethodHandle() {
+    return isReadFromMethodHandle() || isWrittenFromMethodHandle();
+  }
+
   boolean isRead();
 
+  boolean isReadFromMethodHandle();
+
   boolean isReadOnlyIn(DexEncodedMethod method);
 
   boolean isWritten();
 
+  boolean isWrittenFromMethodHandle();
+
   boolean isWrittenInMethodSatisfying(Predicate<DexEncodedMethod> predicate);
 
   boolean isWrittenOnlyInMethodSatisfying(Predicate<DexEncodedMethod> predicate);
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 4715169..b15153b 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoImpl.java
@@ -22,11 +22,15 @@
 
   public static final FieldAccessInfoImpl MISSING_FIELD_ACCESS_INFO = new FieldAccessInfoImpl(null);
 
+  public static int FLAG_IS_READ_FROM_METHOD_HANDLE = 1 << 0;
+  public static int FLAG_IS_WRITTEN_FROM_METHOD_HANDLE = 1 << 1;
+  public static int FLAG_HAS_REFLECTIVE_ACCESS = 1 << 2;
+
   // A direct reference to the definition of the field.
   private DexField field;
 
-  // If this field has a reflective access.
-  private boolean hasReflectiveAccess;
+  // If this field is accessed from a method handle or has a reflective access.
+  private int flags;
 
   // Maps every direct and indirect reference in a read-context to the set of methods in which that
   // reference appears.
@@ -185,11 +189,11 @@
 
   @Override
   public boolean hasReflectiveAccess() {
-    return hasReflectiveAccess;
+    return (flags & FLAG_HAS_REFLECTIVE_ACCESS) != 0;
   }
 
   public void setHasReflectiveAccess() {
-    hasReflectiveAccess = true;
+    flags |= FLAG_HAS_REFLECTIVE_ACCESS;
   }
 
   /** Returns true if this field is read by the program. */
@@ -199,6 +203,15 @@
   }
 
   @Override
+  public boolean isReadFromMethodHandle() {
+    return (flags & FLAG_IS_READ_FROM_METHOD_HANDLE) != 0;
+  }
+
+  public void setReadFromMethodHandle() {
+    flags |= FLAG_IS_READ_FROM_METHOD_HANDLE;
+  }
+
+  @Override
   public boolean isReadOnlyIn(DexEncodedMethod method) {
     assert isRead();
     assert method != null;
@@ -212,6 +225,15 @@
     return writesWithContexts != null && !writesWithContexts.isEmpty();
   }
 
+  @Override
+  public boolean isWrittenFromMethodHandle() {
+    return (flags & FLAG_IS_WRITTEN_FROM_METHOD_HANDLE) != 0;
+  }
+
+  public void setWrittenFromMethodHandle() {
+    flags |= FLAG_IS_WRITTEN_FROM_METHOD_HANDLE;
+  }
+
   /**
    * Returns true if this field is written by a method for which {@param predicate} returns true.
    */
@@ -292,9 +314,7 @@
 
   public FieldAccessInfoImpl rewrittenWithLens(DexDefinitionSupplier definitions, GraphLense lens) {
     FieldAccessInfoImpl rewritten = new FieldAccessInfoImpl(lens.lookupField(field));
-    if (hasReflectiveAccess) {
-      rewritten.setHasReflectiveAccess();
-    }
+    rewritten.flags = flags;
     if (readsWithContexts != null) {
       rewritten.readsWithContexts = new IdentityHashMap<>();
       readsWithContexts.forEach(
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index fb41239..f452556 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -35,16 +35,32 @@
 
   public abstract boolean registerInvokeSuper(DexMethod method);
 
+  public abstract boolean registerInstanceFieldRead(DexField field);
+
+  public boolean registerInstanceFieldReadFromMethodHandle(DexField field) {
+    return registerInstanceFieldRead(field);
+  }
+
   public abstract boolean registerInstanceFieldWrite(DexField field);
 
-  public abstract boolean registerInstanceFieldRead(DexField field);
+  public boolean registerInstanceFieldWriteFromMethodHandle(DexField field) {
+    return registerInstanceFieldWrite(field);
+  }
 
   public abstract boolean registerNewInstance(DexType type);
 
   public abstract boolean registerStaticFieldRead(DexField field);
 
+  public boolean registerStaticFieldReadFromMethodHandle(DexField field) {
+    return registerStaticFieldRead(field);
+  }
+
   public abstract boolean registerStaticFieldWrite(DexField field);
 
+  public boolean registerStaticFieldWriteFromMethodHandle(DexField field) {
+    return registerStaticFieldWrite(field);
+  }
+
   public abstract boolean registerTypeReference(DexType type);
 
   public boolean registerConstClass(DexType type) {
@@ -55,20 +71,19 @@
     return registerTypeReference(type);
   }
 
-  public void registerMethodHandle(
-      DexMethodHandle methodHandle, MethodHandleUse use) {
+  public void registerMethodHandle(DexMethodHandle methodHandle, MethodHandleUse use) {
     switch (methodHandle.type) {
       case INSTANCE_GET:
-        registerInstanceFieldRead(methodHandle.asField());
+        registerInstanceFieldReadFromMethodHandle(methodHandle.asField());
         break;
       case INSTANCE_PUT:
-        registerInstanceFieldWrite(methodHandle.asField());
+        registerInstanceFieldWriteFromMethodHandle(methodHandle.asField());
         break;
       case STATIC_GET:
-        registerStaticFieldRead(methodHandle.asField());
+        registerStaticFieldReadFromMethodHandle(methodHandle.asField());
         break;
       case STATIC_PUT:
-        registerStaticFieldWrite(methodHandle.asField());
+        registerStaticFieldWriteFromMethodHandle(methodHandle.asField());
         break;
       case INVOKE_INSTANCE:
         registerInvokeVirtual(methodHandle.asMethod());
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
index 4064d79..ef36b5b 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -313,7 +313,7 @@
               assert false;
               return;
             }
-            if (!info.hasReflectiveAccess()) {
+            if (!info.hasReflectiveAccess() && !info.isWrittenFromMethodHandle()) {
               info.forEachWriteContext(
                   context ->
                       fieldWrites.computeIfAbsent(context, ignore -> new ArrayList<>()).add(field));
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
index dfcee69..f35fb94 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -65,13 +65,23 @@
   }
 
   @Override
+  public boolean registerInstanceFieldRead(DexField field) {
+    return enqueuer.traceInstanceFieldRead(field, context.getMethod());
+  }
+
+  @Override
+  public boolean registerInstanceFieldReadFromMethodHandle(DexField field) {
+    return enqueuer.traceInstanceFieldReadFromMethodHandle(field, context.getMethod());
+  }
+
+  @Override
   public boolean registerInstanceFieldWrite(DexField field) {
     return enqueuer.traceInstanceFieldWrite(field, context.getMethod());
   }
 
   @Override
-  public boolean registerInstanceFieldRead(DexField field) {
-    return enqueuer.traceInstanceFieldRead(field, context.getMethod());
+  public boolean registerInstanceFieldWriteFromMethodHandle(DexField field) {
+    return enqueuer.traceInstanceFieldWriteFromMethodHandle(field, context.getMethod());
   }
 
   @Override
@@ -85,11 +95,21 @@
   }
 
   @Override
+  public boolean registerStaticFieldReadFromMethodHandle(DexField field) {
+    return enqueuer.traceStaticFieldReadFromMethodHandle(field, context.getMethod());
+  }
+
+  @Override
   public boolean registerStaticFieldWrite(DexField field) {
     return enqueuer.traceStaticFieldWrite(field, context.getMethod());
   }
 
   @Override
+  public boolean registerStaticFieldWriteFromMethodHandle(DexField field) {
+    return enqueuer.traceStaticFieldWriteFromMethodHandle(field, context.getMethod());
+  }
+
+  @Override
   public boolean registerConstClass(DexType type) {
     return enqueuer.traceConstClass(type, context.getMethod());
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 818237c..dc426ea 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -963,6 +963,15 @@
   }
 
   boolean traceInstanceFieldRead(DexField field, DexEncodedMethod currentMethod) {
+    return traceInstanceFieldRead(field, currentMethod, false);
+  }
+
+  boolean traceInstanceFieldReadFromMethodHandle(DexField field, DexEncodedMethod currentMethod) {
+    return traceInstanceFieldRead(field, currentMethod, true);
+  }
+
+  private boolean traceInstanceFieldRead(
+      DexField field, DexEncodedMethod currentMethod, boolean fromMethodHandle) {
     if (!registerFieldRead(field, currentMethod)) {
       return false;
     }
@@ -975,6 +984,10 @@
       return false;
     }
 
+    if (fromMethodHandle) {
+      fieldAccessInfoCollection.get(encodedField.field).setReadFromMethodHandle();
+    }
+
     DexProgramClass clazz = getProgramClassOrNull(encodedField.field.holder);
     if (clazz == null) {
       return false;
@@ -999,6 +1012,15 @@
   }
 
   boolean traceInstanceFieldWrite(DexField field, DexEncodedMethod currentMethod) {
+    return traceInstanceFieldWrite(field, currentMethod, false);
+  }
+
+  boolean traceInstanceFieldWriteFromMethodHandle(DexField field, DexEncodedMethod currentMethod) {
+    return traceInstanceFieldWrite(field, currentMethod, true);
+  }
+
+  private boolean traceInstanceFieldWrite(
+      DexField field, DexEncodedMethod currentMethod, boolean fromMethodHandle) {
     if (!registerFieldWrite(field, currentMethod)) {
       return false;
     }
@@ -1011,6 +1033,10 @@
       return false;
     }
 
+    if (fromMethodHandle) {
+      fieldAccessInfoCollection.get(encodedField.field).setWrittenFromMethodHandle();
+    }
+
     DexProgramClass clazz = getProgramClassOrNull(encodedField.field.holder);
     if (clazz == null) {
       return false;
@@ -1035,6 +1061,15 @@
   }
 
   boolean traceStaticFieldRead(DexField field, DexEncodedMethod currentMethod) {
+    return traceStaticFieldRead(field, currentMethod, false);
+  }
+
+  boolean traceStaticFieldReadFromMethodHandle(DexField field, DexEncodedMethod currentMethod) {
+    return traceStaticFieldRead(field, currentMethod, true);
+  }
+
+  private boolean traceStaticFieldRead(
+      DexField field, DexEncodedMethod currentMethod, boolean fromMethodHandle) {
     if (!registerFieldRead(field, currentMethod)) {
       return false;
     }
@@ -1046,6 +1081,10 @@
       return false;
     }
 
+    if (fromMethodHandle) {
+      fieldAccessInfoCollection.get(encodedField.field).setReadFromMethodHandle();
+    }
+
     if (!isProgramClass(encodedField.field.holder)) {
       // No need to trace into the non-program code.
       return false;
@@ -1079,6 +1118,15 @@
   }
 
   boolean traceStaticFieldWrite(DexField field, DexEncodedMethod currentMethod) {
+    return traceStaticFieldWrite(field, currentMethod, false);
+  }
+
+  boolean traceStaticFieldWriteFromMethodHandle(DexField field, DexEncodedMethod currentMethod) {
+    return traceStaticFieldWrite(field, currentMethod, true);
+  }
+
+  private boolean traceStaticFieldWrite(
+      DexField field, DexEncodedMethod currentMethod, boolean fromMethodHandle) {
     if (!registerFieldWrite(field, currentMethod)) {
       return false;
     }
@@ -1090,6 +1138,10 @@
       return false;
     }
 
+    if (fromMethodHandle) {
+      fieldAccessInfoCollection.get(encodedField.field).setWrittenFromMethodHandle();
+    }
+
     if (!isProgramClass(encodedField.field.holder)) {
       // No need to trace into the non-program code.
       return false;