Version 2.0.51

Cherry pick: Desugared lib wrappers
CL: https://r8-review.googlesource.com/c/r8/+/48100
Hash: 6e13f62cf493197c4760f7472f71fc5bfa8cc525

Change-Id: I88c304aa5c426e24a9b8fc4dc3020bde5d28e4e3
Bug: 151207121
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 7d55176..909219f 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "2.0.50";
+  public static final String LABEL = "2.0.51";
 
   private Version() {
   }
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 4483eca..d45a68c 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
@@ -81,6 +81,10 @@
     }
   }
 
+  public static boolean isVivifiedType(DexType type) {
+    return type.descriptor.toString().startsWith("L" + VIVIFIED_PREFIX);
+  }
+
   public void desugar(IRCode code) {
 
     if (wrapperSynthesizor.hasSynthesized(code.method.method.holder)) {
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 bf48a16..99994fe 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
@@ -19,6 +19,7 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
+import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.FieldAccessFlags;
@@ -31,8 +32,6 @@
 import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterWrapperCfCodeProvider;
 import com.android.tools.r8.ir.synthetic.DesugaredLibraryAPIConversionCfCodeProvider.APIConverterWrapperConversionCfCodeProvider;
 import com.android.tools.r8.origin.SynthesizedOrigin;
-import com.android.tools.r8.utils.Box;
-import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
@@ -45,8 +44,6 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
-import java.util.function.BiConsumer;
-import java.util.function.BiFunction;
 
 // I am responsible for the generation of wrappers used to call library APIs when desugaring
 // libraries. Wrappers can be both ways, wrapping the desugarType as a type, or the type as
@@ -98,37 +95,34 @@
   public static final String VIVIFIED_TYPE_WRAPPER_SUFFIX = "$-V-WRP";
 
   private final AppView<?> appView;
-  private final Map<DexType, Pair<DexType, DexProgramClass>> typeWrappers =
-      new ConcurrentHashMap<>();
-  private final Map<DexType, Pair<DexType, DexProgramClass>> vivifiedTypeWrappers =
-      new ConcurrentHashMap<>();
+  private final DexString dexWrapperPrefix;
+  private final Map<DexType, DexType> typeWrappers = new ConcurrentHashMap<>();
+  private final Map<DexType, DexType> vivifiedTypeWrappers = new ConcurrentHashMap<>();
   // The invalidWrappers are wrappers with incorrect behavior because of final methods that could
   // not be overridden. Such wrappers are awful because the runtime behavior is undefined and does
   // not raise explicit errors. So we register them here and conversion methods for such wrappers
   // raise a runtime exception instead of generating the wrapper.
   private final Set<DexType> invalidWrappers = Sets.newConcurrentHashSet();
-  private final Set<DexType> generatedWrappers = Sets.newConcurrentHashSet();
   private final DexItemFactory factory;
   private final DesugaredLibraryAPIConverter converter;
 
   DesugaredLibraryWrapperSynthesizer(AppView<?> appView, DesugaredLibraryAPIConverter converter) {
     this.appView = appView;
     this.factory = appView.dexItemFactory();
+    this.dexWrapperPrefix = factory.createString("L" + WRAPPER_PREFIX);
     this.converter = converter;
   }
 
-  public static boolean isSynthesizedWrapper(DexType clazz) {
-    return clazz.descriptor.toString().contains(WRAPPER_PREFIX);
+  public static boolean isSynthesizedWrapper(DexType type) {
+    // Slow path, but more convenient since no instance is needed. Use hasSynthesized(DexType) when
+    // possible.
+    return type.descriptor.toString().startsWith("L" + WRAPPER_PREFIX);
   }
 
   boolean hasSynthesized(DexType type) {
-    return generatedWrappers.contains(type);
+    return type.descriptor.startsWith(dexWrapperPrefix);
   }
 
-  // Wrapper initial generation section.
-  // 1. Generate wrappers without conversion methods.
-  // 2. Compute wrapper types.
-
   boolean canGenerateWrapper(DexType type) {
     DexClass dexClass = appView.definitionFor(type);
     if (dexClass == null) {
@@ -138,15 +132,11 @@
   }
 
   DexType getTypeWrapper(DexType type) {
-    return getWrapper(type, TYPE_WRAPPER_SUFFIX, typeWrappers, this::generateTypeWrapper);
+    return getWrapper(type, TYPE_WRAPPER_SUFFIX, typeWrappers);
   }
 
   DexType getVivifiedTypeWrapper(DexType type) {
-    return getWrapper(
-        type,
-        VIVIFIED_TYPE_WRAPPER_SUFFIX,
-        vivifiedTypeWrappers,
-        this::generateVivifiedTypeWrapper);
+    return getWrapper(type, VIVIFIED_TYPE_WRAPPER_SUFFIX, vivifiedTypeWrappers);
   }
 
   private DexType createWrapperType(DexType type, String suffix) {
@@ -164,46 +154,27 @@
             + ";");
   }
 
-  private DexType getWrapper(
-      DexType type,
-      String suffix,
-      Map<DexType, Pair<DexType, DexProgramClass>> wrappers,
-      BiFunction<DexClass, DexType, DexProgramClass> wrapperGenerator) {
-    // Answers the DexType of the wrapper. Generate the wrapper DexProgramClass if not already done,
-    // except the conversions methods. Conversion method generation is postponed to know if the
-    // reverse wrapper is present at generation time.
-    // We generate the type while locking the concurrent hash map, but we release the lock before
-    // generating the actual class to avoid locking for too long (hence the Pair).
+  private DexType getWrapper(DexType type, String suffix, Map<DexType, DexType> wrappers) {
     assert !type.toString().startsWith(DesugaredLibraryAPIConverter.VIVIFIED_PREFIX);
-    Box<Boolean> toGenerate = new Box<>(false);
-    Pair<DexType, DexProgramClass> pair =
-        wrappers.computeIfAbsent(
-            type,
-            t -> {
-              toGenerate.set(true);
-              DexType wrapperType = createWrapperType(type, suffix);
-              generatedWrappers.add(wrapperType);
-              return new Pair<>(wrapperType, null);
-            });
-    if (toGenerate.get()) {
-      assert pair.getSecond() == null;
-      DexClass dexClass = appView.definitionFor(type);
-      // The dexClass should be a library class, so it cannot be null.
-      assert dexClass != null
-          && (dexClass.isLibraryClass() || appView.options().isDesugaredLibraryCompilation());
-      if (dexClass.accessFlags.isFinal()) {
-        throw appView
-            .options()
-            .reporter
-            .fatalError(
-                new StringDiagnostic(
-                    "Cannot generate a wrapper for final class "
-                        + dexClass.type
-                        + ". Add a custom conversion in the desugared library."));
-      }
-      pair.setSecond(wrapperGenerator.apply(dexClass, pair.getFirst()));
+    return wrappers.computeIfAbsent(type, t -> createWrapperType(type, suffix));
+  }
+
+  private DexClass getValidClassToWrap(DexType type) {
+    DexClass dexClass = appView.definitionFor(type);
+    // The dexClass should be a library class, so it cannot be null.
+    assert dexClass != null
+        && (dexClass.isLibraryClass() || appView.options().isDesugaredLibraryCompilation());
+    if (dexClass.accessFlags.isFinal()) {
+      throw appView
+          .options()
+          .reporter
+          .fatalError(
+              new StringDiagnostic(
+                  "Cannot generate a wrapper for final class "
+                      + dexClass.type
+                      + ". Add a custom conversion in the desugared library."));
     }
-    return pair.getFirst();
+    return dexClass;
   }
 
   private DexType vivifiedTypeFor(DexType type) {
@@ -212,11 +183,12 @@
 
   private DexProgramClass generateTypeWrapper(DexClass dexClass, DexType typeWrapperType) {
     DexType type = dexClass.type;
-    DexEncodedField wrapperField = synthesizeWrappedValueField(typeWrapperType, type);
+    DexEncodedField wrapperField = synthesizeWrappedValueEncodedField(typeWrapperType, type);
     return synthesizeWrapper(
         vivifiedTypeFor(type),
         dexClass,
         synthesizeVirtualMethodsForTypeWrapper(dexClass, wrapperField),
+        generateTypeConversion(type, typeWrapperType),
         wrapperField);
   }
 
@@ -224,11 +196,12 @@
       DexClass dexClass, DexType vivifiedTypeWrapperType) {
     DexType type = dexClass.type;
     DexEncodedField wrapperField =
-        synthesizeWrappedValueField(vivifiedTypeWrapperType, vivifiedTypeFor(type));
+        synthesizeWrappedValueEncodedField(vivifiedTypeWrapperType, vivifiedTypeFor(type));
     return synthesizeWrapper(
         type,
         dexClass,
         synthesizeVirtualMethodsForVivifiedTypeWrapper(dexClass, wrapperField),
+        generateVivifiedTypeConversion(type, vivifiedTypeWrapperType),
         wrapperField);
   }
 
@@ -236,6 +209,7 @@
       DexType wrappingType,
       DexClass clazz,
       DexEncodedMethod[] virtualMethods,
+      DexEncodedMethod conversionMethod,
       DexEncodedField wrapperField) {
     boolean isItf = clazz.isInterface();
     DexType superType = isItf ? factory.objectType : wrappingType;
@@ -257,9 +231,7 @@
         DexAnnotationSet.empty(),
         DexEncodedField.EMPTY_ARRAY, // No static fields.
         new DexEncodedField[] {wrapperField},
-        new DexEncodedMethod[] {
-          synthesizeConstructor(wrapperField.field)
-        }, // Conversions methods will be added later.
+        new DexEncodedMethod[] {synthesizeConstructor(wrapperField.field), conversionMethod},
         virtualMethods,
         factory.getSkipNameValidationForTesting(),
         getChecksumSupplier(this, clazz.type),
@@ -446,8 +418,12 @@
     return implementedMethods;
   }
 
-  private DexEncodedField synthesizeWrappedValueField(DexType holder, DexType fieldType) {
-    DexField field = factory.createField(holder, fieldType, factory.wrapperFieldName);
+  private DexField wrappedValueField(DexType holder, DexType fieldType) {
+    return factory.createField(holder, fieldType, factory.wrapperFieldName);
+  }
+
+  private DexEncodedField synthesizeWrappedValueEncodedField(DexType holder, DexType fieldType) {
+    DexField field = wrappedValueField(holder, fieldType);
     // Field is package private to be accessible from convert methods without a getter.
     FieldAccessFlags fieldAccessFlags =
         FieldAccessFlags.fromCfAccessFlags(Constants.ACC_FINAL | Constants.ACC_SYNTHETIC);
@@ -479,86 +455,70 @@
         true);
   }
 
-  // Wrapper finalization section.
-  // 1. Generate conversions methods (convert(type)).
-  // 2. Add the synthesized classes.
-  // 3. Process all methods.
-
   void finalizeWrappers(
       DexApplication.Builder<?> builder, IRConverter irConverter, ExecutorService executorService)
       throws ExecutionException {
-    assert verifyAllClassesGenerated();
-    // We register first, then optimize to avoid warnings due to missing parameter types.
-    registerWrappers(builder, typeWrappers);
-    registerWrappers(builder, vivifiedTypeWrappers);
-    finalizeWrappers(irConverter, executorService, typeWrappers, this::generateTypeConversions);
-    finalizeWrappers(
-        irConverter,
-        executorService,
-        vivifiedTypeWrappers,
-        this::generateVivifiedTypeConversions);
+    List<DexProgramClass> synthesizedWrappers = generateWrappers();
+    registerAndProcessWrappers(builder, irConverter, executorService, synthesizedWrappers);
   }
 
-  private void registerWrappers(
-      DexApplication.Builder<?> builder, Map<DexType, Pair<DexType, DexProgramClass>> wrappers) {
-    for (DexType type : wrappers.keySet()) {
-      DexProgramClass pgrmClass = wrappers.get(type).getSecond();
-      assert pgrmClass != null;
-      registerSynthesizedClass(pgrmClass, builder);
+  private List<DexProgramClass> generateWrappers() {
+    List<DexProgramClass> synthesizedWrappers = new ArrayList<>();
+    Set<DexType> synthesizedWrapperTypes = Sets.newIdentityHashSet();
+    // Generating a wrapper may require other wrappers to be generated, iterate until fix point.
+    while (synthesizedWrapperTypes.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);
+        }
+      }
+      for (DexType type : vivifiedTypeWrappers.keySet()) {
+        DexType vivifiedTypeWrapperType = vivifiedTypeWrappers.get(type);
+        if (!synthesizedWrapperTypes.contains(vivifiedTypeWrapperType)) {
+          synthesizedWrappers.add(
+              generateVivifiedTypeWrapper(getValidClassToWrap(type), vivifiedTypeWrapperType));
+          synthesizedWrapperTypes.add(vivifiedTypeWrapperType);
+        }
+      }
     }
+    assert synthesizedWrappers.size() == synthesizedWrapperTypes.size();
+    return synthesizedWrappers;
   }
 
-  private void finalizeWrappers(
+  private void registerAndProcessWrappers(
+      DexApplication.Builder<?> builder,
       IRConverter irConverter,
       ExecutorService executorService,
-      Map<DexType, Pair<DexType, DexProgramClass>> wrappers,
-      BiConsumer<DexType, DexProgramClass> generateConversions)
+      List<DexProgramClass> wrappers)
       throws ExecutionException {
-    for (DexType type : wrappers.keySet()) {
-      DexProgramClass pgrmClass = wrappers.get(type).getSecond();
-      assert pgrmClass != null;
-      generateConversions.accept(type, pgrmClass);
-      irConverter.optimizeSynthesizedClass(pgrmClass, executorService);
+    for (DexProgramClass wrapper : wrappers) {
+      builder.addSynthesizedClass(wrapper, false);
+      appView.appInfo().addSynthesizedClass(wrapper);
     }
+    irConverter.optimizeSynthesizedClasses(wrappers, executorService);
   }
 
-  private boolean verifyAllClassesGenerated() {
-    for (Pair<DexType, DexProgramClass> pair : vivifiedTypeWrappers.values()) {
-      assert pair.getSecond() != null;
-    }
-    for (Pair<DexType, DexProgramClass> pair : typeWrappers.values()) {
-      assert pair.getSecond() != null;
-    }
-    return true;
+  private DexEncodedMethod generateTypeConversion(DexType type, DexType typeWrapperType) {
+    DexType reverse = vivifiedTypeWrappers.get(type);
+    return synthesizeConversionMethod(
+        typeWrapperType,
+        type,
+        type,
+        vivifiedTypeFor(type),
+        reverse == null ? null : wrappedValueField(reverse, vivifiedTypeFor(type)));
   }
 
-  private void registerSynthesizedClass(
-      DexProgramClass synthesizedClass, DexApplication.Builder<?> builder) {
-    builder.addSynthesizedClass(synthesizedClass, false);
-    appView.appInfo().addSynthesizedClass(synthesizedClass);
-  }
-
-  private void generateTypeConversions(DexType type, DexProgramClass synthesizedClass) {
-    Pair<DexType, DexProgramClass> reverse = vivifiedTypeWrappers.get(type);
-    assert reverse == null || reverse.getSecond() != null;
-    synthesizedClass.addDirectMethod(
-        synthesizeConversionMethod(
-            synthesizedClass.type,
-            type,
-            type,
-            vivifiedTypeFor(type),
-            reverse == null ? null : reverse.getSecond()));
-  }
-
-  private void generateVivifiedTypeConversions(DexType type, DexProgramClass synthesizedClass) {
-    Pair<DexType, DexProgramClass> reverse = typeWrappers.get(type);
-    synthesizedClass.addDirectMethod(
-        synthesizeConversionMethod(
-            synthesizedClass.type,
-            type,
-            vivifiedTypeFor(type),
-            type,
-            reverse == null ? null : reverse.getSecond()));
+  private DexEncodedMethod generateVivifiedTypeConversion(
+      DexType type, DexType vivifiedTypeWrapperType) {
+    DexType reverse = typeWrappers.get(type);
+    return synthesizeConversionMethod(
+        vivifiedTypeWrapperType,
+        type,
+        vivifiedTypeFor(type),
+        type,
+        reverse == null ? null : wrappedValueField(reverse, type));
   }
 
   private DexEncodedMethod synthesizeConversionMethod(
@@ -566,7 +526,7 @@
       DexType type,
       DexType argType,
       DexType returnType,
-      DexClass reverseWrapperClassOrNull) {
+      DexField reverseFieldOrNull) {
     DexMethod method =
         factory.createMethod(
             holder, factory.createProto(returnType, argType), factory.convertMethodName);
@@ -582,15 +542,11 @@
                   holder)
               .generateCfCode();
     } else {
-      DexField uniqueFieldOrNull =
-          reverseWrapperClassOrNull == null
-              ? null
-              : reverseWrapperClassOrNull.instanceFields().get(0).field;
       cfCode =
           new APIConverterWrapperConversionCfCodeProvider(
                   appView,
                   argType,
-                  uniqueFieldOrNull,
+                  reverseFieldOrNull,
                   factory.createField(holder, returnType, factory.wrapperFieldName))
               .generateCfCode();
     }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 7620ac7..5ff8e57 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -1042,27 +1042,28 @@
     return false;
   }
 
-  public void warnMissingInterface(
-      DexClass classToDesugar, DexClass implementing, DexType missing) {
+  private boolean shouldIgnoreFromReports(DexType missing) {
+    return appView.rewritePrefix.hasRewrittenType(missing)
+        || DesugaredLibraryWrapperSynthesizer.isSynthesizedWrapper(missing)
+        || DesugaredLibraryAPIConverter.isVivifiedType(missing)
+        || isCompanionClassType(missing)
+        || emulatedInterfaces.containsValue(missing)
+        || options.desugaredLibraryConfiguration.getCustomConversions().containsValue(missing);
+  }
+
+  void warnMissingInterface(DexClass classToDesugar, DexClass implementing, DexType missing) {
     // We use contains() on non hashed collection, but we know it's a 8 cases collection.
     // j$ interfaces won't be missing, they are in the desugared library.
-    if (!emulatedInterfaces.values().contains(missing)) {
-      options.warningMissingInterfaceForDesugar(classToDesugar, implementing, missing);
+    if (shouldIgnoreFromReports(missing)) {
+      return;
     }
+    options.warningMissingInterfaceForDesugar(classToDesugar, implementing, missing);
   }
 
   private void warnMissingType(DexMethod referencedFrom, DexType missing) {
     // Companion/Emulated interface/Conversion classes for desugared library won't be missing,
     // they are in the desugared library.
-    if (appView.rewritePrefix.hasRewrittenType(missing)
-        || DesugaredLibraryWrapperSynthesizer.isSynthesizedWrapper(missing)
-        || isCompanionClassType(missing)
-        || appView
-            .options()
-            .desugaredLibraryConfiguration
-            .getCustomConversions()
-            .values()
-            .contains(missing)) {
+    if (shouldIgnoreFromReports(missing)) {
       return;
     }
     DexMethod method = appView.graphLense().getOriginalMethodSignature(referencedFrom);