Desugared lib: register wrapper field access

Still crashed on Clock tests

Bug: 148448927
Change-Id: Ieb8ce2a0f041c04cd21343a0039295e36877edbe
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
index cb4baef..830479c 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
@@ -27,6 +27,14 @@
     return type;
   }
 
+  public boolean isInstanceOf() {
+    return true;
+  }
+
+  public CfInstanceOf asInstanceOf() {
+    return this;
+  }
+
   @Override
   public void write(MethodVisitor visitor, NamingLens lens) {
     visitor.visitTypeInsn(Opcodes.INSTANCEOF, lens.lookupInternalName(type));
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
index 833e5e1..43d209e 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
@@ -95,6 +95,14 @@
     return null;
   }
 
+  public boolean isInstanceOf() {
+    return false;
+  }
+
+  public CfInstanceOf asInstanceOf() {
+    return null;
+  }
+
   public boolean isStore() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java
index bb21829..61d7abe 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java
@@ -23,14 +23,20 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
 
 public class DesugaredLibraryConversionWrapperAnalysis extends EnqueuerAnalysis
     implements EnqueuerInvokeAnalysis {
+
+  private final AppView<?> appView;
   private final DesugaredLibraryAPIConverter converter;
   private boolean callbackGenerated = false;
-  private boolean wrappersGenerated = false;
+  private Map<DexProgramClass, DexProgramClass> wrappersToReverseMap = null;
 
   public DesugaredLibraryConversionWrapperAnalysis(AppView<?> appView) {
+    this.appView = appView;
     this.converter =
         new DesugaredLibraryAPIConverter(appView, Mode.GENERATE_CALLBACKS_AND_WRAPPERS);
   }
@@ -75,18 +81,17 @@
     return converter.generateCallbackMethods();
   }
 
-  public List<DexProgramClass> generateWrappers() {
-    assert !wrappersGenerated;
-    wrappersGenerated = true;
-    return converter.generateWrappers();
+  public Set<DexProgramClass> generateWrappers() {
+    assert wrappersToReverseMap == null;
+    wrappersToReverseMap = converter.synthesizeWrappersAndMapToReverse();
+    return wrappersToReverseMap.keySet();
   }
 
   // Generate a mock classpath class for all vivified types.
   // Types will be available at runtime in the desugared library dex file.
-  public List<DexClasspathClass> generateWrappersSuperTypeMock(
-      List<DexProgramClass> wrappers, AppView<?> appView) {
+  public List<DexClasspathClass> generateWrappersSuperTypeMock() {
     List<DexClasspathClass> classpathClasses = new ArrayList<>();
-    for (DexProgramClass wrapper : wrappers) {
+    for (DexProgramClass wrapper : wrappersToReverseMap.keySet()) {
       boolean mockIsInterface = wrapper.interfaces.size() == 1;
       DexType mockType = mockIsInterface ? wrapper.interfaces.values[0] : wrapper.superType;
       if (appView.definitionFor(mockType) == null) {
@@ -117,4 +122,38 @@
     }
     return classpathClasses;
   }
+
+  public DesugaredLibraryConversionWrapperAnalysis registerWrite(
+      DexProgramClass wrapper, Consumer<DexEncodedMethod> registration) {
+    registration.accept(getInitializer(wrapper));
+    return this;
+  }
+
+  public DesugaredLibraryConversionWrapperAnalysis registerReads(
+      DexProgramClass wrapper, Consumer<DexEncodedMethod> registration) {
+    // The field of each wrapper is read exclusively in all virtual methods and the reverse wrapper
+    // convert method.
+    for (DexEncodedMethod virtualMethod : wrapper.virtualMethods()) {
+      registration.accept(virtualMethod);
+    }
+    DexProgramClass reverseWrapper = wrappersToReverseMap.get(wrapper);
+    if (reverseWrapper != null) {
+      registration.accept(getConvertMethod(reverseWrapper));
+    }
+    return this;
+  }
+
+  private DexEncodedMethod getInitializer(DexProgramClass wrapper) {
+    DexEncodedMethod initializer =
+        wrapper.lookupDirectMethod(DexEncodedMethod::isInstanceInitializer);
+    assert initializer != null;
+    return initializer;
+  }
+
+  private DexEncodedMethod getConvertMethod(DexProgramClass wrapper) {
+    DexEncodedMethod convertMethod = wrapper.lookupDirectMethod(DexEncodedMethod::isStatic);
+    assert convertMethod != null;
+    assert convertMethod.method.name == appView.dexItemFactory().convertMethodName;
+    return convertMethod;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
index 0db6f28..5df6a03 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
@@ -299,8 +299,8 @@
     return result;
   }
 
-  public List<DexProgramClass> generateWrappers() {
-    return wrapperSynthesizor.generateWrappers();
+  public Map<DexProgramClass, DexProgramClass> synthesizeWrappersAndMapToReverse() {
+    return wrapperSynthesizor.synthesizeWrappersAndMapToReverse();
   }
 
   private List<DexEncodedMethod> generateCallbackMethods(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
index 5b3b25a..a1b217a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
@@ -36,7 +36,10 @@
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -466,40 +469,59 @@
   void finalizeWrappersForD8(
       DexApplication.Builder<?> builder, IRConverter irConverter, ExecutorService executorService)
       throws ExecutionException {
-    List<DexProgramClass> synthesizedWrappers = generateWrappers();
-    registerAndProcessWrappers(builder, irConverter, executorService, synthesizedWrappers);
+    Map<DexType, DexProgramClass> synthesizedWrappers = synthesizeWrappers();
+    registerAndProcessWrappers(builder, irConverter, executorService, synthesizedWrappers.values());
   }
 
-  List<DexProgramClass> generateWrappers() {
-    List<DexProgramClass> synthesizedWrappers = new ArrayList<>();
-    Set<DexType> synthesizedWrapperTypes = Sets.newIdentityHashSet();
+  private Map<DexType, DexProgramClass> synthesizeWrappers() {
+    Map<DexType, DexProgramClass> synthesizedWrappers = new IdentityHashMap<>();
     // Generating a wrapper may require other wrappers to be generated, iterate until fix point.
-    while (synthesizedWrapperTypes.size() != typeWrappers.size() + vivifiedTypeWrappers.size()) {
+    while (synthesizedWrappers.size() != typeWrappers.size() + vivifiedTypeWrappers.size()) {
       for (DexType type : typeWrappers.keySet()) {
         DexType typeWrapperType = typeWrappers.get(type);
-        if (!synthesizedWrapperTypes.contains(typeWrapperType)) {
-          synthesizedWrappers.add(generateTypeWrapper(getValidClassToWrap(type), typeWrapperType));
-          synthesizedWrapperTypes.add(typeWrapperType);
+        if (!synthesizedWrappers.containsKey(typeWrapperType)) {
+          synthesizedWrappers.put(
+              typeWrapperType, generateTypeWrapper(getValidClassToWrap(type), typeWrapperType));
         }
       }
       for (DexType type : vivifiedTypeWrappers.keySet()) {
         DexType vivifiedTypeWrapperType = vivifiedTypeWrappers.get(type);
-        if (!synthesizedWrapperTypes.contains(vivifiedTypeWrapperType)) {
-          synthesizedWrappers.add(
+        if (!synthesizedWrappers.containsKey(vivifiedTypeWrapperType)) {
+          synthesizedWrappers.put(
+              vivifiedTypeWrapperType,
               generateVivifiedTypeWrapper(getValidClassToWrap(type), vivifiedTypeWrapperType));
-          synthesizedWrapperTypes.add(vivifiedTypeWrapperType);
         }
       }
     }
-    assert synthesizedWrappers.size() == synthesizedWrapperTypes.size();
     return synthesizedWrappers;
   }
 
+  private Map<DexType, DexType> reverseWrapperMap() {
+    Map<DexType, DexType> reverseWrapperMap = new IdentityHashMap<>();
+    for (DexType type : typeWrappers.keySet()) {
+      reverseWrapperMap.put(typeWrappers.get(type), vivifiedTypeWrappers.get(type));
+    }
+    for (DexType type : vivifiedTypeWrappers.keySet()) {
+      reverseWrapperMap.put(vivifiedTypeWrappers.get(type), typeWrappers.get(type));
+    }
+    return reverseWrapperMap;
+  }
+
+  Map<DexProgramClass, DexProgramClass> synthesizeWrappersAndMapToReverse() {
+    Map<DexType, DexProgramClass> synthesizedWrappers = synthesizeWrappers();
+    Map<DexType, DexType> reverseMap = reverseWrapperMap();
+    Map<DexProgramClass, DexProgramClass> wrappersAndReverse = new IdentityHashMap<>();
+    for (DexProgramClass wrapper : synthesizedWrappers.values()) {
+      wrappersAndReverse.put(wrapper, synthesizedWrappers.get(reverseMap.get(wrapper.type)));
+    }
+    return wrappersAndReverse;
+  }
+
   private void registerAndProcessWrappers(
       DexApplication.Builder<?> builder,
       IRConverter irConverter,
       ExecutorService executorService,
-      List<DexProgramClass> wrappers)
+      Collection<DexProgramClass> wrappers)
       throws ExecutionException {
     for (DexProgramClass wrapper : wrappers) {
       builder.addSynthesizedClass(wrapper, false);
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 0a2c6a3..1782239 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -2402,7 +2402,7 @@
     }
 
     // Generate the wrappers.
-    List<DexProgramClass> wrappers = desugaredLibraryWrapperAnalysis.generateWrappers();
+    Set<DexProgramClass> wrappers = desugaredLibraryWrapperAnalysis.generateWrappers();
     for (DexProgramClass wrapper : wrappers) {
       appBuilder.addProgramClass(wrapper);
       liveTypes.add(wrapper, graphReporter.fakeReportShouldNotBeUsed());
@@ -2412,12 +2412,20 @@
         targetedMethods.add(method, graphReporter.fakeReportShouldNotBeUsed());
         liveMethods.add(wrapper, method, graphReporter.fakeReportShouldNotBeUsed());
       }
+      // Register wrapper unique field reads and unique write.
+      assert wrapper.instanceFields().size() == 1;
+      DexField field = wrapper.instanceFields().get(0).field;
+      FieldAccessInfoImpl info = new FieldAccessInfoImpl(field);
+      fieldAccessInfoCollection.extend(field, info);
+      desugaredLibraryWrapperAnalysis
+          .registerWrite(wrapper, writeContext -> info.recordWrite(field, writeContext))
+          .registerReads(wrapper, readContext -> info.recordRead(field, readContext));
     }
 
     // Add all vivified types as classpath classes.
     // They will be available at runtime in the desugared library dex file.
     List<DexClasspathClass> mockVivifiedClasses =
-        desugaredLibraryWrapperAnalysis.generateWrappersSuperTypeMock(wrappers, appView);
+        desugaredLibraryWrapperAnalysis.generateWrappersSuperTypeMock();
     appBuilder.addClasspathClasses(mockVivifiedClasses);
   }