Generate wrappers on demand in D8

Bug: 189912077
Change-Id: I4e689f0b5ca568e40286321b7c6d8f9323f2c7b1
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 c89d93c..5fc1537 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
@@ -407,11 +407,11 @@
     }
     DexType returnType = invokedMethod.proto.returnType;
     if (appView.rewritePrefix.hasRewrittenType(returnType, appView) && canConvert(returnType)) {
-      registerConversionWrappers(returnType, vivifiedTypeFor(returnType, appView));
+      registerConversionWrappers(returnType);
     }
     for (DexType argType : invokedMethod.proto.parameters.values) {
       if (appView.rewritePrefix.hasRewrittenType(argType, appView) && canConvert(argType)) {
-        registerConversionWrappers(argType, argType);
+        registerConversionWrappers(argType);
       }
     }
   }
@@ -547,7 +547,7 @@
 
   private Instruction createParameterConversion(
       IRCode code, DexType argType, DexType argVivifiedType, Value inValue) {
-    DexMethod conversionMethod = createConversionMethod(argType, argType, argVivifiedType);
+    DexMethod conversionMethod = ensureConversionMethod(argType, argType, argVivifiedType);
     // The value is null only if the input is null.
     Value convertedValue =
         createConversionValue(code, inValue.getType().nullability(), argVivifiedType, null);
@@ -556,7 +556,7 @@
 
   private Instruction createReturnConversionAndReplaceUses(
       IRCode code, InvokeMethod invokeMethod, DexType returnType, DexType returnVivifiedType) {
-    DexMethod conversionMethod = createConversionMethod(returnType, returnVivifiedType, returnType);
+    DexMethod conversionMethod = ensureConversionMethod(returnType, returnVivifiedType, returnType);
     Value outValue = invokeMethod.outValue();
     Value convertedValue =
         createConversionValue(code, Nullability.maybeNull(), returnType, outValue.getLocalInfo());
@@ -567,17 +567,13 @@
     return new InvokeStatic(conversionMethod, convertedValue, Collections.singletonList(outValue));
   }
 
-  private void registerConversionWrappers(DexType type, DexType srcType) {
+  private void registerConversionWrappers(DexType type) {
     if (appView.options().desugaredLibraryConfiguration.getCustomConversions().get(type) == null) {
-      if (type == srcType) {
-        wrapperSynthesizor.getTypeWrapper(type);
-      } else {
-        wrapperSynthesizor.getVivifiedTypeWrapper(type);
-      }
+      wrapperSynthesizor.registerWrapper(type);
     }
   }
 
-  public DexMethod createConversionMethod(DexType type, DexType srcType, DexType destType) {
+  public DexMethod ensureConversionMethod(DexType type, DexType srcType, DexType destType) {
     // ConversionType holds the methods "rewrittenType convert(type)" and the other way around.
     // But everything is going to be rewritten, so we need to use vivifiedType and type".
     DexType conversionHolder =
@@ -585,8 +581,8 @@
     if (conversionHolder == null) {
       conversionHolder =
           type == srcType
-              ? wrapperSynthesizor.getTypeWrapper(type)
-              : wrapperSynthesizor.getVivifiedTypeWrapper(type);
+              ? wrapperSynthesizor.ensureTypeWrapper(type)
+              : wrapperSynthesizor.ensureVivifiedTypeWrapper(type);
     }
     assert conversionHolder != null;
     return factory.createMethod(
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 c13bfc1..00c8bff 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
@@ -117,26 +117,17 @@
     return appView.options().desugaredLibraryConfiguration.getWrapperConversions().contains(type);
   }
 
-  DexType getTypeWrapper(DexType type) {
-    return registerWrapper(type, SyntheticKind.WRAPPER);
+  DexType ensureTypeWrapper(DexType type) {
+    return ensureWrappers(type).getWrapper().type;
   }
 
-  DexType getVivifiedTypeWrapper(DexType type) {
-    return registerWrapper(type, SyntheticKind.VIVIFIED_WRAPPER);
+  DexType ensureVivifiedTypeWrapper(DexType type) {
+    return ensureWrappers(type).getVivifiedWrapper().type;
   }
 
-  private DexType registerWrapper(DexType type, SyntheticKind kind) {
-    assert canGenerateWrapper(type) : type;
+  public void registerWrapper(DexType type) {
     wrappersToGenerate.add(type);
-    DexClass dexClass = getValidClassToWrap(type);
-    assert dexClass != null;
-    DexType wrapperType =
-        appView.getSyntheticItems().getFixedSyntheticTypeWhileMigrating(kind, dexClass, appView);
-    assert converter.canGenerateWrappersAndCallbacks()
-            || (appView.definitionFor(wrapperType) != null
-                && appView.definitionFor(wrapperType).isClasspathClass())
-        : "Wrapper " + wrapperType + " should have been generated in the enqueuer.";
-    return wrapperType;
+    assert getValidClassToWrap(type) != null;
   }
 
   private DexClass getValidClassToWrap(DexType type) {
@@ -152,12 +143,36 @@
     return DesugaredLibraryAPIConverter.vivifiedTypeFor(type, appView);
   }
 
-  private void ensureWrappers(DexClass context, Consumer<DexClasspathClass> creationCallback) {
+  static class Wrappers {
+    private final DexClass wrapper;
+    private final DexClass vivifiedWrapper;
+
+    Wrappers(DexClass wrapper, DexClass vivifiedWrapper) {
+      this.wrapper = wrapper;
+      this.vivifiedWrapper = vivifiedWrapper;
+    }
+
+    public DexClass getWrapper() {
+      return wrapper;
+    }
+
+    public DexClass getVivifiedWrapper() {
+      return vivifiedWrapper;
+    }
+  }
+
+  private Wrappers ensureWrappers(DexType type) {
+    assert canGenerateWrapper(type) : type;
+    DexClass dexClass = getValidClassToWrap(type);
+    return ensureWrappers(dexClass, ignored -> {});
+  }
+
+  private Wrappers ensureWrappers(DexClass context, Consumer<DexClasspathClass> creationCallback) {
     DexType type = context.type;
     DexClass wrapper;
     DexClass vivifiedWrapper;
-    if (appView.options().isDesugaredLibraryCompilation()) {
-      assert context.isProgramClass();
+    if (context.isProgramClass()) {
+      assert appView.options().isDesugaredLibraryCompilation();
       DexProgramClass programContext = context.asProgramClass();
       wrapper =
           ensureProgramWrapper(
@@ -210,11 +225,16 @@
           vivifiedWrapperField,
           wrapperField);
     }
+    return new Wrappers(wrapper, vivifiedWrapper);
+  }
+
+  private DexEncodedField getWrapperUniqueEncodedField(DexClass wrapper) {
+    assert wrapper.instanceFields().size() == 1;
+    return wrapper.instanceFields().get(0);
   }
 
   private DexField getWrapperUniqueField(DexClass wrapper) {
-    assert wrapper.instanceFields().size() == 1;
-    return wrapper.instanceFields().get(0).getReference();
+    return getWrapperUniqueEncodedField(wrapper).getReference();
   }
 
   private DexProgramClass ensureProgramWrapper(
@@ -229,10 +249,12 @@
             kind,
             programContext,
             appView,
-            builder ->
-                buildWrapper(
-                    wrappingType, wrappedType, programContext, virtualMethodProvider, builder),
-            ignored -> {});
+            builder -> buildWrapper(wrappingType, wrappedType, programContext, builder),
+            // The creation of virtual methods may require new wrappers, this needs to happen
+            // once the wrapper is created to avoid infinite recursion.
+            wrapper ->
+                wrapper.setVirtualMethods(
+                    virtualMethodProvider.apply(getWrapperUniqueEncodedField(wrapper))));
   }
 
   private DexClasspathClass ensureClasspathWrapper(
@@ -250,12 +272,14 @@
             appView,
             builder ->
                 buildWrapper(
-                    wrappingType,
-                    wrappedType,
-                    classpathOrLibraryContext.asDexClass(),
-                    virtualMethodProvider,
-                    builder),
-            creationCallback);
+                    wrappingType, wrappedType, classpathOrLibraryContext.asDexClass(), builder),
+            // The creation of virtual methods may require new wrappers, this needs to happen
+            // once the wrapper is created to avoid infinite recursion.
+            wrapper -> {
+              wrapper.setVirtualMethods(
+                  virtualMethodProvider.apply(getWrapperUniqueEncodedField(wrapper)));
+              creationCallback.accept(wrapper);
+            });
   }
 
   private ProgramMethod ensureProgramConversionMethod(
@@ -329,7 +353,6 @@
       DexType wrappingType,
       DexType wrappedType,
       DexClass clazz,
-      Function<DexEncodedField, DexEncodedMethod[]> virtualMethodProvider,
       SyntheticClassBuilder<?, ?> builder) {
     boolean isItf = clazz.isInterface();
     DexType superType = isItf ? factory.objectType : wrappingType;
@@ -341,8 +364,7 @@
         .setInterfaces(interfaces)
         .setSuperType(superType)
         .setInstanceFields(Collections.singletonList(wrapperField))
-        .addMethod(methodBuilder -> buildWrapperConstructor(wrapperField, methodBuilder))
-        .setVirtualMethods(Arrays.asList(virtualMethodProvider.apply(wrapperField)));
+        .addMethod(methodBuilder -> buildWrapperConstructor(wrapperField, methodBuilder));
   }
 
   private void buildWrapperConstructor(
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
index 04fa965..254511e 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
@@ -98,7 +98,7 @@
           instructions.add(
               new CfInvoke(
                   Opcodes.INVOKESTATIC,
-                  converter.createConversionMethod(param, param, vivifiedTypeFor(param)),
+                  converter.ensureConversionMethod(param, param, vivifiedTypeFor(param)),
                   false));
           newParameters[index - 1] = vivifiedTypeFor(param);
         }
@@ -129,7 +129,7 @@
         instructions.add(
             new CfInvoke(
                 Opcodes.INVOKESTATIC,
-                converter.createConversionMethod(
+                converter.ensureConversionMethod(
                     returnType, vivifiedTypeFor(returnType), returnType),
                 false));
       }
@@ -185,7 +185,7 @@
           instructions.add(
               new CfInvoke(
                   Opcodes.INVOKESTATIC,
-                  converter.createConversionMethod(param, vivifiedTypeFor(param), param),
+                  converter.ensureConversionMethod(param, vivifiedTypeFor(param), param),
                   false));
         }
         if (param == factory.longType || param == factory.doubleType) {
@@ -205,7 +205,7 @@
         instructions.add(
             new CfInvoke(
                 Opcodes.INVOKESTATIC,
-                converter.createConversionMethod(
+                converter.ensureConversionMethod(
                     returnType, returnType, vivifiedTypeFor(returnType)),
                 false));
         returnType = vivifiedTypeFor(returnType);
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 3e68dae..f7e235f 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -564,15 +564,6 @@
     }
   }
 
-  public DexType getFixedSyntheticTypeWhileMigrating(
-      SyntheticKind kind, DexClass context, AppView<?> appView) {
-    SynthesizingContext outerContext =
-        context.isProgramClass()
-            ? getSynthesizingContext(context.asProgramClass(), appView)
-            : SynthesizingContext.fromNonSyntheticInputContext(context.asClasspathOrLibraryClass());
-    return SyntheticNaming.createFixedType(kind, outerContext, appView.dexItemFactory());
-  }
-
   public ProgramMethod ensureFixedClassMethod(
       DexString name,
       DexProto proto,
@@ -627,8 +618,8 @@
           new SyntheticClasspathClassBuilder(type, kind, outerContext, appView.dexItemFactory());
       classConsumer.accept(classBuilder);
       DexClasspathClass clazz = classBuilder.build();
-      onCreationConsumer.accept(clazz);
       addPendingDefinition(new SyntheticClasspathClassDefinition(kind, outerContext, clazz));
+      onCreationConsumer.accept(clazz);
       return clazz;
     }
   }