Introduce rewrite derived prefix

- fix the google3 build

Bug: 184026720
Change-Id: I9025a9f983287811f93b24a5e8678f8651f76492
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
index e01def6..a612aea 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
@@ -18,6 +18,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.function.Consumer;
 
 public class HumanDesugaredLibrarySpecificationParser {
@@ -35,6 +36,7 @@
   static final String WRAPPER_CONVERSION_KEY = "wrapper_conversion";
   static final String CUSTOM_CONVERSION_KEY = "custom_conversion";
   static final String REWRITE_PREFIX_KEY = "rewrite_prefix";
+  static final String REWRITE_DERIVED_PREFIX_KEY = "rewrite_derived_prefix";
   static final String RETARGET_LIB_MEMBER_KEY = "retarget_lib_member";
   static final String EMULATE_INTERFACE_KEY = "emulate_interface";
   static final String DONT_REWRITE_KEY = "dont_rewrite";
@@ -191,6 +193,16 @@
         builder.putRewritePrefix(rewritePrefix.getKey(), rewritePrefix.getValue().getAsString());
       }
     }
+    if (jsonFlagSet.has(REWRITE_DERIVED_PREFIX_KEY)) {
+      for (Map.Entry<String, JsonElement> prefixToMatch :
+          jsonFlagSet.get(REWRITE_DERIVED_PREFIX_KEY).getAsJsonObject().entrySet()) {
+        for (Entry<String, JsonElement> rewriteRule :
+            prefixToMatch.getValue().getAsJsonObject().entrySet()) {
+          builder.putRewriteDerivedPrefix(
+              prefixToMatch.getKey(), rewriteRule.getKey(), rewriteRule.getValue().getAsString());
+        }
+      }
+    }
     if (jsonFlagSet.has(RETARGET_LIB_MEMBER_KEY)) {
       for (Map.Entry<String, JsonElement> retarget :
           jsonFlagSet.get(RETARGET_LIB_MEMBER_KEY).getAsJsonObject().entrySet()) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
index bf87cd8..3faaf25 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.ImmutableMap;
@@ -27,6 +26,7 @@
 public class HumanRewritingFlags {
 
   private final Map<String, String> rewritePrefix;
+  private final Map<String, Map<String, String>> rewriteDerivedPrefix;
   private final Map<DexType, DexType> emulateLibraryInterface;
   private final Map<DexMethod, DexType> retargetCoreLibMember;
   private final Map<DexType, DexType> backportCoreLibraryMember;
@@ -37,6 +37,7 @@
 
   HumanRewritingFlags(
       Map<String, String> rewritePrefix,
+      Map<String, Map<String, String>> rewriteDerivedPrefix,
       Map<DexType, DexType> emulateLibraryInterface,
       Map<DexMethod, DexType> retargetCoreLibMember,
       Map<DexType, DexType> backportCoreLibraryMember,
@@ -45,6 +46,7 @@
       Set<DexType> dontRetargetLibMember,
       Set<DexType> wrapperConversions) {
     this.rewritePrefix = rewritePrefix;
+    this.rewriteDerivedPrefix = rewriteDerivedPrefix;
     this.emulateLibraryInterface = emulateLibraryInterface;
     this.retargetCoreLibMember = retargetCoreLibMember;
     this.backportCoreLibraryMember = backportCoreLibraryMember;
@@ -61,18 +63,12 @@
         ImmutableMap.of(),
         ImmutableMap.of(),
         ImmutableMap.of(),
+        ImmutableMap.of(),
         ImmutableSet.of(),
         ImmutableSet.of(),
         ImmutableSet.of());
   }
 
-  public static HumanRewritingFlags withOnlyRewritePrefixForTesting(
-      Map<String, String> prefix, InternalOptions options) {
-    Builder builder = builder(options.dexItemFactory(), options.reporter, Origin.unknown());
-    prefix.forEach(builder::putRewritePrefix);
-    return builder.build();
-  }
-
   public static Builder builder(DexItemFactory dexItemFactory, Reporter reporter, Origin origin) {
     return new Builder(dexItemFactory, reporter, origin);
   }
@@ -83,6 +79,7 @@
         reporter,
         origin,
         rewritePrefix,
+        rewriteDerivedPrefix,
         emulateLibraryInterface,
         retargetCoreLibMember,
         backportCoreLibraryMember,
@@ -96,6 +93,10 @@
     return rewritePrefix;
   }
 
+  public Map<String, Map<String, String>> getRewriteDerivedPrefix() {
+    return rewriteDerivedPrefix;
+  }
+
   public Map<DexType, DexType> getEmulateLibraryInterface() {
     return emulateLibraryInterface;
   }
@@ -133,6 +134,7 @@
     private final Origin origin;
 
     private final Map<String, String> rewritePrefix;
+    Map<String, Map<String, String>> rewriteDerivedPrefix;
     private final Map<DexType, DexType> emulateLibraryInterface;
     private final Map<DexMethod, DexType> retargetCoreLibMember;
     private final Map<DexType, DexType> backportCoreLibraryMember;
@@ -147,6 +149,7 @@
           reporter,
           origin,
           new HashMap<>(),
+          new HashMap<>(),
           new IdentityHashMap<>(),
           new IdentityHashMap<>(),
           new IdentityHashMap<>(),
@@ -161,6 +164,7 @@
         Reporter reporter,
         Origin origin,
         Map<String, String> rewritePrefix,
+        Map<String, Map<String, String>> rewriteDerivedPrefix,
         Map<DexType, DexType> emulateLibraryInterface,
         Map<DexMethod, DexType> retargetCoreLibMember,
         Map<DexType, DexType> backportCoreLibraryMember,
@@ -172,6 +176,7 @@
       this.reporter = reporter;
       this.origin = origin;
       this.rewritePrefix = new HashMap<>(rewritePrefix);
+      this.rewriteDerivedPrefix = new HashMap<>(rewriteDerivedPrefix);
       this.emulateLibraryInterface = new IdentityHashMap<>(emulateLibraryInterface);
       this.retargetCoreLibMember = new IdentityHashMap<>(retargetCoreLibMember);
       this.backportCoreLibraryMember = new IdentityHashMap<>(backportCoreLibraryMember);
@@ -209,6 +214,18 @@
       return this;
     }
 
+    public Builder putRewriteDerivedPrefix(
+        String prefixToMatch, String prefixToRewrite, String rewrittenPrefix) {
+      Map<String, String> map =
+          rewriteDerivedPrefix.computeIfAbsent(prefixToMatch, k -> new HashMap<>());
+      put(
+          map,
+          prefixToRewrite,
+          rewrittenPrefix,
+          HumanDesugaredLibrarySpecificationParser.REWRITE_DERIVED_PREFIX_KEY);
+      return this;
+    }
+
     public Builder putEmulateLibraryInterface(
         String emulateLibraryItf, String rewrittenEmulateLibraryItf) {
       DexType interfaceType = stringClassToDexType(emulateLibraryItf);
@@ -329,6 +346,7 @@
       validate();
       return new HumanRewritingFlags(
           ImmutableMap.copyOf(rewritePrefix),
+          ImmutableMap.copyOf(rewriteDerivedPrefix),
           ImmutableMap.copyOf(emulateLibraryInterface),
           ImmutableMap.copyOf(retargetCoreLibMember),
           ImmutableMap.copyOf(backportCoreLibraryMember),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java
index c79fad3..d79540c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java
@@ -76,6 +76,7 @@
       HumanRewritingFlags.Builder commonBuilder,
       HumanRewritingFlags.Builder builder) {
     deduplicateRewritePrefix(flags, otherFlags, commonBuilder, builder);
+    deduplicateRewriteDifferentPrefix(flags, otherFlags, commonBuilder, builder);
 
     deduplicateFlags(
         flags.getEmulateLibraryInterface(),
@@ -115,6 +116,33 @@
         builder::addWrapperConversion);
   }
 
+  private static void deduplicateRewriteDifferentPrefix(
+      HumanRewritingFlags flags,
+      HumanRewritingFlags otherFlags,
+      HumanRewritingFlags.Builder commonBuilder,
+      HumanRewritingFlags.Builder builder) {
+    flags
+        .getRewriteDerivedPrefix()
+        .forEach(
+            (prefixToMatch, rewriteRules) -> {
+              if (!otherFlags.getRewriteDerivedPrefix().containsKey(prefixToMatch)) {
+                rewriteRules.forEach(
+                    (k, v) -> builder.putRewriteDerivedPrefix(prefixToMatch, k, v));
+              } else {
+                Map<String, String> otherMap =
+                    otherFlags.getRewriteDerivedPrefix().get(prefixToMatch);
+                rewriteRules.forEach(
+                    (k, v) -> {
+                      if (otherMap.containsKey(k) && otherMap.get(k).equals(v)) {
+                        commonBuilder.putRewriteDerivedPrefix(prefixToMatch, k, v);
+                      } else {
+                        builder.putRewriteDerivedPrefix(prefixToMatch, k, v);
+                      }
+                    });
+              }
+            });
+  }
+
   private static void deduplicateRewritePrefix(
       HumanRewritingFlags flags,
       HumanRewritingFlags otherFlags,
@@ -124,7 +152,7 @@
         .getRewritePrefix()
         .forEach(
             (k, v) -> {
-              if (otherFlags.getRewritePrefix().get(k) != null
+              if (otherFlags.getRewritePrefix().containsKey(k)
                   && otherFlags.getRewritePrefix().get(k).equals(v)) {
                 commonBuilder.putRewritePrefix(k, v);
               } else {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java
index fd76244..dd22fe7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java
@@ -67,6 +67,13 @@
           if (!flags.getRewritePrefix().isEmpty()) {
             toJson.put(REWRITE_PREFIX_KEY, new TreeMap<>(flags.getRewritePrefix()));
           }
+          if (!flags.getRewriteDerivedPrefix().isEmpty()) {
+            TreeMap<String, Map<String, String>> rewriteDerivedPrefix = new TreeMap<>();
+            flags
+                .getRewriteDerivedPrefix()
+                .forEach((k, v) -> rewriteDerivedPrefix.put(k, new TreeMap<>(v)));
+            toJson.put(REWRITE_DERIVED_PREFIX_KEY, rewriteDerivedPrefix);
+          }
           if (!flags.getEmulateLibraryInterface().isEmpty()) {
             toJson.put(EMULATE_INTERFACE_KEY, mapToString(flags.getEmulateLibraryInterface()));
           }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
index 5704f3d..0fc22f1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
@@ -14,7 +14,6 @@
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Sets;
-import java.util.IdentityHashMap;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.BiConsumer;
@@ -25,18 +24,20 @@
   private final MachineRewritingFlags.Builder builder;
   private final String synthesizedPrefix;
   private final Map<DexString, DexString> descriptorPrefix;
-  private final Map<DexType, DexType> reverse = new IdentityHashMap<>();
+  private final Map<DexString, Map<DexString, DexString>> descriptorDifferentPrefix;
   private final Set<DexString> usedPrefix = Sets.newIdentityHashSet();
 
   public HumanToMachinePrefixConverter(
       AppInfoWithClassHierarchy appInfo,
       MachineRewritingFlags.Builder builder,
       String synthesizedPrefix,
-      Map<String, String> descriptorPrefix) {
+      HumanRewritingFlags rewritingFlags) {
     this.appInfo = appInfo;
     this.builder = builder;
     this.synthesizedPrefix = synthesizedPrefix;
-    this.descriptorPrefix = convertRewritePrefix(descriptorPrefix);
+    this.descriptorPrefix = convertRewritePrefix(rewritingFlags.getRewritePrefix());
+    this.descriptorDifferentPrefix =
+        convertRewriteDifferentPrefix(rewritingFlags.getRewriteDerivedPrefix());
   }
 
   public void convertPrefixFlags(
@@ -46,28 +47,17 @@
     rewriteValues(rewritingFlags.getCustomConversions());
     rewriteEmulatedInterface(rewritingFlags.getEmulateLibraryInterface());
     rewriteRetargetKeys(rewritingFlags.getRetargetCoreLibMember());
-    rewriteReverse();
     warnIfUnusedPrefix(warnConsumer);
   }
 
   private void warnIfUnusedPrefix(BiConsumer<String, Set<DexString>> warnConsumer) {
     Set<DexString> prefixes = Sets.newIdentityHashSet();
     prefixes.addAll(descriptorPrefix.keySet());
+    prefixes.addAll(descriptorDifferentPrefix.keySet());
     prefixes.removeAll(usedPrefix);
     warnConsumer.accept("The following prefixes do not match any type: ", prefixes);
   }
 
-  // For custom conversions, this is responsible in rewriting backward.
-  private void rewriteReverse() {
-    reverse.forEach(
-        (rewrittenType, type) -> {
-          DexType chainType = rewrittenType(rewrittenType);
-          if (chainType != null) {
-            builder.rewriteType(rewrittenType, chainType);
-          }
-        });
-  }
-
   public DexType convertJavaNameToDesugaredLibrary(DexType type) {
     String convertedPrefix = DescriptorUtils.getJavaTypeFromBinaryName(synthesizedPrefix);
     String interfaceType = type.toString();
@@ -90,11 +80,6 @@
     emulateLibraryInterface.forEach(builder::rewriteDerivedTypeOnly);
   }
 
-  private void rewriteType(DexType type, DexType rewrittenType) {
-    builder.rewriteType(type, rewrittenType);
-    reverse.put(rewrittenType, type);
-  }
-
   private void rewriteValues(
       Map<?, DexType> flags) {
     for (DexType type : flags.values()) {
@@ -104,46 +89,73 @@
 
   private void rewriteClasses() {
     for (DexClass clazz : appInfo.app().asDirect().libraryClasses()) {
-      rewriteClass(clazz);
+      registerType(clazz.type);
+      registerDifferentType(clazz.type);
     }
     for (DexClass clazz : appInfo.classes()) {
-      rewriteClass(clazz);
-    }
-  }
-
-  private void rewriteClass(DexClass clazz) {
-    registerType(clazz.type);
-    // We allow missing referenced types for the work-in-progress desugaring.
-    if (clazz.superType != null) {
-      registerType(clazz.superType);
-    }
-    clazz.interfaces.forEach(this::registerType);
-    if (clazz.getInnerClasses() != null) {
-      clazz.getInnerClasses().forEach(attr -> attr.forEachType(this::registerType));
+      registerType(clazz.type);
+      registerDifferentType(clazz.type);
     }
   }
 
   private void registerType(DexType type) {
     DexType rewrittenType = rewrittenType(type);
     if (rewrittenType != null) {
-      rewriteType(type, rewrittenType);
+      builder.rewriteType(type, rewrittenType);
     }
   }
 
-  private DexType rewrittenType(DexType type) {
+  private void registerDifferentType(DexType type) {
+    DexString prefix = prefixMatching(type, descriptorDifferentPrefix.keySet());
+    if (prefix == null) {
+      return;
+    }
+    descriptorDifferentPrefix
+        .get(prefix)
+        .forEach(
+            (k, v) -> {
+              DexString typeDescriptor =
+                  type.descriptor.withNewPrefix(prefix, k, appInfo.dexItemFactory());
+              DexString rewrittenTypeDescriptor =
+                  type.descriptor.withNewPrefix(prefix, v, appInfo.dexItemFactory());
+              builder.rewriteType(
+                  appInfo.dexItemFactory().createType(typeDescriptor),
+                  appInfo.dexItemFactory().createType(rewrittenTypeDescriptor));
+            });
+    usedPrefix.add(prefix);
+  }
+
+  private DexString prefixMatching(DexType type, Set<DexString> prefixes) {
     DexString prefixToMatch = type.descriptor.withoutArray(appInfo.dexItemFactory());
-    for (DexString prefix : descriptorPrefix.keySet()) {
+    for (DexString prefix : prefixes) {
       if (prefixToMatch.startsWith(prefix)) {
-        DexString rewrittenTypeDescriptor =
-            type.descriptor.withNewPrefix(
-                prefix, descriptorPrefix.get(prefix), appInfo.dexItemFactory());
-        usedPrefix.add(prefix);
-        return appInfo.dexItemFactory().createType(rewrittenTypeDescriptor);
+        return prefix;
       }
     }
     return null;
   }
 
+  private DexType rewrittenType(DexType type) {
+    DexString prefix = prefixMatching(type, descriptorPrefix.keySet());
+    if (prefix == null) {
+      return null;
+    }
+    DexString rewrittenTypeDescriptor =
+        type.descriptor.withNewPrefix(
+            prefix, descriptorPrefix.get(prefix), appInfo.dexItemFactory());
+    usedPrefix.add(prefix);
+    return appInfo.dexItemFactory().createType(rewrittenTypeDescriptor);
+  }
+
+  private ImmutableMap<DexString, Map<DexString, DexString>> convertRewriteDifferentPrefix(
+      Map<String, Map<String, String>> rewriteDerivedPrefix) {
+    ImmutableMap.Builder<DexString, Map<DexString, DexString>> mapBuilder = ImmutableMap.builder();
+    for (String key : rewriteDerivedPrefix.keySet()) {
+      mapBuilder.put(toDescriptorPrefix(key), convertRewritePrefix(rewriteDerivedPrefix.get(key)));
+    }
+    return mapBuilder.build();
+  }
+
   private ImmutableMap<DexString, DexString> convertRewritePrefix(
       Map<String, String> rewritePrefix) {
     ImmutableMap.Builder<DexString, DexString> mapBuilder = ImmutableMap.builder();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
index c668858..1b5009a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
@@ -104,8 +104,7 @@
         .convertRetargetFlags(rewritingFlags, builder, this::warnMissingReferences);
     new HumanToMachineEmulatedInterfaceConverter(appInfo)
         .convertEmulatedInterfaces(rewritingFlags, appInfo, builder, this::warnMissingReferences);
-    new HumanToMachinePrefixConverter(
-            appInfo, builder, synthesizedPrefix, rewritingFlags.getRewritePrefix())
+    new HumanToMachinePrefixConverter(appInfo, builder, synthesizedPrefix, rewritingFlags)
         .convertPrefixFlags(rewritingFlags, this::warnMissingDexString);
     new HumanToMachineWrapperConverter(appInfo)
         .convertWrappers(rewritingFlags, builder, this::warnMissingReferences);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
index c4884a5..fcdef84 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
@@ -34,6 +34,7 @@
 import com.android.tools.r8.utils.Pair;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 import java.io.IOException;
@@ -45,6 +46,7 @@
 
 public class LegacyToHumanSpecificationConverter {
 
+  private static final String wrapperPrefix = "__wrapper__.";
   private AndroidApiLevel legacyHackLevel = AndroidApiLevel.N_MR1;
 
   public void convertAllAPILevels(
@@ -182,7 +184,9 @@
     HumanRewritingFlags.Builder builder =
         HumanRewritingFlags.builder(app.dexItemFactory(), app.options.reporter, origin);
 
-    flags.getRewritePrefix().forEach(builder::putRewritePrefix);
+    flags
+        .getRewritePrefix()
+        .forEach((prefix, rewritten) -> rewritePrefix(builder, prefix, rewritten));
     flags.getEmulateLibraryInterface().forEach(builder::putEmulateLibraryInterface);
     flags.getBackportCoreLibraryMember().forEach(builder::putBackportCoreLibraryMember);
     flags.getCustomConversions().forEach(builder::putCustomConversion);
@@ -199,6 +203,28 @@
     return builder.build();
   }
 
+  private void rewritePrefix(HumanRewritingFlags.Builder builder, String prefix, String rewritten) {
+    // Legacy hacks: The human specification matches on class' types so we need different
+    // rewritings.
+    if (prefix.startsWith("j$")) {
+      assert rewritten.startsWith("java");
+      builder.putRewriteDerivedPrefix(rewritten, prefix, rewritten);
+      return;
+    }
+    if (prefix.equals(wrapperPrefix)) {
+      // We hard code here this applies to java.nio and java.io only.
+      ImmutableMap<String, String> map =
+          ImmutableMap.of("java.nio.", "j$.nio.", "java.io.", "j$.io.");
+      map.forEach(
+          (k, v) -> {
+            builder.putRewriteDerivedPrefix(k, wrapperPrefix + k, k);
+            builder.putRewriteDerivedPrefix(k, wrapperPrefix + v, v);
+          });
+      return;
+    }
+    builder.putRewritePrefix(prefix, rewritten);
+  }
+
   private void convertDontRewriteInvocation(
       HumanRewritingFlags.Builder builder, DexApplication app, Pair<DexType, DexString> pair) {
     DexClass dexClass = app.definitionFor(pair.getFirst());
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
index 4d861a0..08dbd18 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
@@ -98,6 +98,9 @@
       HumanRewritingFlags humanRewritingFlags1, HumanRewritingFlags humanRewritingFlags2) {
     assertEquals(humanRewritingFlags1.getRewritePrefix(), humanRewritingFlags2.getRewritePrefix());
     assertEquals(
+        humanRewritingFlags1.getRewriteDerivedPrefix(),
+        humanRewritingFlags2.getRewriteDerivedPrefix());
+    assertEquals(
         humanRewritingFlags1.getBackportCoreLibraryMember(),
         humanRewritingFlags2.getBackportCoreLibraryMember());
     assertEquals(