RewritePrefix flag in Machine specification

Bug: 184026720
Change-Id: I37bb23bc4626823af55ce6257e48e8e44b11dfa7
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 3b3e48e..ad3e92b 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -168,8 +168,7 @@
   private static AppView<AppInfo> readApp(
       AndroidApp inputApp, InternalOptions options, ExecutorService executor, Timing timing)
       throws IOException {
-    PrefixRewritingMapper rewritePrefix =
-        options.desugaredLibrarySpecification.getPrefixRewritingMapper();
+    PrefixRewritingMapper rewritePrefix = options.getPrefixRewritingMapper();
     ApplicationReader applicationReader = new ApplicationReader(inputApp, options, timing);
     LazyLoadedDexApplication app = applicationReader.read(executor);
     AppInfo appInfo = AppInfo.createInitialAppInfo(app, applicationReader.readMainDexClasses(app));
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index c3bf91f..9ba6c7f 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -166,8 +166,7 @@
     LazyLoadedDexApplication lazyApp =
         new ApplicationReader(inputApp, options, timing).read(executor);
 
-    PrefixRewritingMapper rewritePrefix =
-        options.desugaredLibrarySpecification.getPrefixRewritingMapper();
+    PrefixRewritingMapper rewritePrefix = options.getPrefixRewritingMapper();
 
     DexApplication app = new L8TreePruner(options).prune(lazyApp, rewritePrefix);
     return AppView.createForL8(AppInfo.createInitialAppInfo(app), rewritePrefix);
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index c2316e4..14b0ded 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -160,7 +160,7 @@
 
   private static <T extends AppInfo> PrefixRewritingMapper defaultPrefixRewritingMapper(T appInfo) {
     InternalOptions options = appInfo.options();
-    return options.desugaredLibrarySpecification.getPrefixRewritingMapper();
+    return options.getPrefixRewritingMapper();
   }
 
   public static <T extends AppInfo> AppView<T> createForD8(T appInfo) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index ee7f8e1..49ad698 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -100,8 +100,7 @@
   public static List<DexMethod> generateListOfBackportedMethods(
       AndroidApp androidApp, InternalOptions options, ExecutorService executor) throws IOException {
     List<DexMethod> methods = new ArrayList<>();
-    PrefixRewritingMapper rewritePrefix =
-        options.desugaredLibrarySpecification.getPrefixRewritingMapper();
+    PrefixRewritingMapper rewritePrefix = options.getPrefixRewritingMapper();
     AppInfo appInfo = null;
     if (androidApp != null) {
       DexApplication app =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java b/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
index f5861d7..bf96b67 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/PrefixRewritingMapper.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Sets;
@@ -220,6 +221,55 @@
     }
   }
 
+  public static class MachineDesugarPrefixRewritingMapper extends PrefixRewritingMapper {
+
+    private final PrefixRewritingMapper mapper;
+    private final Map<DexType, DexType> rewriteType;
+    private final Map<DexType, DexType> rewriteDerivedTypeOnly;
+
+    public MachineDesugarPrefixRewritingMapper(
+        PrefixRewritingMapper mapper, MachineRewritingFlags flags) {
+      this.mapper = mapper;
+      this.rewriteType = new ConcurrentHashMap<>(flags.getRewriteType());
+      rewriteDerivedTypeOnly = flags.getRewriteDerivedTypeOnly();
+    }
+
+    @Override
+    public DexType rewrittenType(DexType type, AppView<?> appView) {
+      assert mapper.rewrittenType(type, appView) == rewriteType.get(type);
+      return rewriteType.get(type);
+    }
+
+    @Override
+    public DexType rewrittenContextType(DexType context, AppView<?> appView) {
+      if (rewriteType.containsKey(context)) {
+        return rewriteType.get(context);
+      }
+      return rewriteDerivedTypeOnly.get(context);
+    }
+
+    @Override
+    public void rewriteType(DexType type, DexType rewrittenType) {
+      mapper.rewriteType(type, rewrittenType);
+      rewriteType.compute(
+          type,
+          (t, val) -> {
+            assert val == null || val == rewrittenType;
+            return rewrittenType;
+          });
+    }
+
+    @Override
+    public boolean isRewriting() {
+      return true;
+    }
+
+    @Override
+    public void forAllRewrittenTypes(Consumer<DexType> consumer) {
+      rewriteType.keySet().forEach(consumer);
+    }
+  }
+
   public static class EmptyPrefixRewritingMapper extends PrefixRewritingMapper {
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
index db17cf5..4077e4b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.google.common.collect.ImmutableMap;
+import java.util.IdentityHashMap;
 import java.util.Map;
 
 public class MachineRewritingFlags {
@@ -16,16 +17,25 @@
   }
 
   MachineRewritingFlags(
+      Map<DexType, DexType> rewriteType,
+      Map<DexType, DexType> rewriteDerivedTypeOnly,
       Map<DexMethod, DexMethod> staticRetarget,
       Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget,
       Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget,
       Map<DexType, EmulatedInterfaceDescriptor> emulatedInterfaces) {
+    this.rewriteType = rewriteType;
+    this.rewriteDerivedTypeOnly = rewriteDerivedTypeOnly;
     this.staticRetarget = staticRetarget;
     this.nonEmulatedVirtualRetarget = nonEmulatedVirtualRetarget;
     this.emulatedVirtualRetarget = emulatedVirtualRetarget;
     this.emulatedInterfaces = emulatedInterfaces;
   }
 
+  // Rewrites all the references to the keys as well as synthetic types derived from any key.
+  private final Map<DexType, DexType> rewriteType;
+  // Rewrites only synthetic types derived from any key.
+  private final Map<DexType, DexType> rewriteDerivedTypeOnly;
+
   // Static methods to retarget, duplicated to library boundaries.
   private final Map<DexMethod, DexMethod> staticRetarget;
 
@@ -43,6 +53,14 @@
   // Emulated interface descriptors.
   private final Map<DexType, EmulatedInterfaceDescriptor> emulatedInterfaces;
 
+  public Map<DexType, DexType> getRewriteType() {
+    return rewriteType;
+  }
+
+  public Map<DexType, DexType> getRewriteDerivedTypeOnly() {
+    return rewriteDerivedTypeOnly;
+  }
+
   public Map<DexMethod, DexMethod> getStaticRetarget() {
     return staticRetarget;
   }
@@ -63,6 +81,8 @@
 
     Builder() {}
 
+    private final Map<DexType, DexType> rewriteType = new IdentityHashMap<>();
+    private final Map<DexType, DexType> rewriteDerivedTypeOnly = new IdentityHashMap<>();
     private final ImmutableMap.Builder<DexMethod, DexMethod> staticRetarget =
         ImmutableMap.builder();
     private final ImmutableMap.Builder<DexMethod, DexMethod> nonEmulatedVirtualRetarget =
@@ -72,6 +92,14 @@
     private final ImmutableMap.Builder<DexType, EmulatedInterfaceDescriptor> emulatedInterfaces =
         ImmutableMap.builder();
 
+    public void rewriteType(DexType src, DexType target) {
+      rewriteType.put(src, target);
+    }
+
+    public void rewriteDerivedTypeOnly(DexType src, DexType target) {
+      rewriteDerivedTypeOnly.put(src, target);
+    }
+
     public void putStaticRetarget(DexMethod src, DexMethod dest) {
       staticRetarget.put(src, dest);
     }
@@ -90,6 +118,8 @@
 
     public MachineRewritingFlags build() {
       return new MachineRewritingFlags(
+          rewriteType,
+          rewriteDerivedTypeOnly,
           staticRetarget.build(),
           nonEmulatedVirtualRetarget.build(),
           emulatedVirtualRetarget.build(),
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
new file mode 100644
index 0000000..69c409a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
@@ -0,0 +1,112 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+
+public class HumanToMachinePrefixConverter {
+
+  private final AppInfoWithClassHierarchy appInfo;
+
+  public HumanToMachinePrefixConverter(AppInfoWithClassHierarchy appInfo) {
+    this.appInfo = appInfo;
+  }
+
+  private DexString toDescriptorPrefix(String prefix) {
+    return appInfo
+        .dexItemFactory()
+        .createString("L" + DescriptorUtils.getBinaryNameFromJavaType(prefix));
+  }
+
+  public void convertPrefixFlags(
+      HumanRewritingFlags rewritingFlags,
+      MachineRewritingFlags.Builder builder,
+      String synthesizedPrefix) {
+    Map<DexString, DexString> descriptorPrefix = convertRewritePrefix(rewritingFlags);
+    rewriteClasses(descriptorPrefix, builder);
+    rewriteValues(descriptorPrefix, builder, rewritingFlags.getRetargetCoreLibMember());
+    rewriteValues(descriptorPrefix, builder, rewritingFlags.getCustomConversions());
+    rewriteEmulatedInterface(builder, rewritingFlags.getEmulateLibraryInterface());
+    rewriteRetargetKeys(builder, rewritingFlags.getRetargetCoreLibMember(), synthesizedPrefix);
+  }
+
+  public DexType convertJavaNameToDesugaredLibrary(DexType type, String prefix) {
+    String convertedPrefix = DescriptorUtils.getJavaTypeFromBinaryName(prefix);
+    String interfaceType = type.toString();
+    int firstPackage = interfaceType.indexOf('.');
+    return appInfo
+        .dexItemFactory()
+        .createType(
+            DescriptorUtils.javaTypeToDescriptor(
+                convertedPrefix + interfaceType.substring(firstPackage + 1)));
+  }
+
+  private void rewriteRetargetKeys(
+      MachineRewritingFlags.Builder builder, Map<DexMethod, DexType> retarget, String prefix) {
+    for (DexMethod dexMethod : retarget.keySet()) {
+      DexType type = convertJavaNameToDesugaredLibrary(dexMethod.holder, prefix);
+      builder.rewriteDerivedTypeOnly(dexMethod.holder, type);
+    }
+  }
+
+  private void rewriteEmulatedInterface(
+      MachineRewritingFlags.Builder builder, Map<DexType, DexType> emulateLibraryInterface) {
+    emulateLibraryInterface.forEach(builder::rewriteDerivedTypeOnly);
+  }
+
+  private void rewriteValues(
+      Map<DexString, DexString> descriptorPrefix,
+      MachineRewritingFlags.Builder builder,
+      Map<?, DexType> flags) {
+    for (DexType type : flags.values()) {
+      DexType rewrittenType = rewrittenType(descriptorPrefix, type);
+      builder.rewriteType(type, rewrittenType);
+    }
+  }
+
+  private void rewriteClasses(
+      Map<DexString, DexString> descriptorPrefix, MachineRewritingFlags.Builder builder) {
+    for (DexProgramClass clazz : appInfo.classes()) {
+      DexType type = clazz.type;
+      DexType rewrittenType = rewrittenType(descriptorPrefix, type);
+      if (rewrittenType == null) {
+        continue;
+      }
+      builder.rewriteType(type, rewrittenType);
+    }
+  }
+
+  private DexType rewrittenType(Map<DexString, DexString> descriptorPrefix, DexType type) {
+    DexString prefixToMatch = type.descriptor.withoutArray(appInfo.dexItemFactory());
+    for (DexString prefix : descriptorPrefix.keySet()) {
+      if (prefixToMatch.startsWith(prefix)) {
+        DexString rewrittenTypeDescriptor =
+            type.descriptor.withNewPrefix(
+                prefix, descriptorPrefix.get(prefix), appInfo.dexItemFactory());
+        return appInfo.dexItemFactory().createType(rewrittenTypeDescriptor);
+      }
+    }
+    return null;
+  }
+
+  private ImmutableMap<DexString, DexString> convertRewritePrefix(
+      HumanRewritingFlags rewritingFlags) {
+    Map<String, String> rewritePrefix = rewritingFlags.getRewritePrefix();
+    ImmutableMap.Builder<DexString, DexString> mapBuilder = ImmutableMap.builder();
+    for (String key : rewritePrefix.keySet()) {
+      mapBuilder.put(toDescriptorPrefix(key), toDescriptorPrefix(rewritePrefix.get(key)));
+    }
+    return mapBuilder.build();
+  }
+}
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 69ae2e2..af380d4 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
@@ -29,17 +29,24 @@
     DexApplication app = readApp(androidLib, options);
     AppView<?> appView = AppView.createForD8(AppInfo.createInitialAppInfo(app));
     MachineRewritingFlags machineRewritingFlags =
-        convertRewritingFlags(humanSpec.getRewritingFlags(), appView.appInfoForDesugaring());
+        convertRewritingFlags(
+            humanSpec.getSynthesizedLibraryClassesPackagePrefix(),
+            humanSpec.getRewritingFlags(),
+            appView.appInfoForDesugaring());
     return new MachineDesugaredLibrarySpecification(
         humanSpec.isLibraryCompilation(), machineRewritingFlags);
   }
 
   private MachineRewritingFlags convertRewritingFlags(
-      HumanRewritingFlags rewritingFlags, AppInfoWithClassHierarchy appInfo) {
+      String synthesizedPrefix,
+      HumanRewritingFlags rewritingFlags,
+      AppInfoWithClassHierarchy appInfo) {
     MachineRewritingFlags.Builder builder = MachineRewritingFlags.builder();
     new HumanToMachineRetargetConverter(appInfo).convertRetargetFlags(rewritingFlags, builder);
     new HumanToMachineEmulatedInterfaceConverter(appInfo)
         .convertEmulatedInterfaces(rewritingFlags, appInfo, builder);
+    new HumanToMachinePrefixConverter(appInfo)
+        .convertPrefixFlags(rewritingFlags, builder, synthesizedPrefix);
     return builder.build();
   }
 
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index c1dc2c9..58cdf7e 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -53,6 +53,8 @@
 import com.android.tools.r8.horizontalclassmerging.Policy;
 import com.android.tools.r8.inspector.internal.InspectorImpl;
 import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
+import com.android.tools.r8.ir.desugar.PrefixRewritingMapper.MachineDesugarPrefixRewritingMapper;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.nest.Nest;
@@ -892,6 +894,15 @@
   public LegacyDesugaredLibrarySpecification desugaredLibrarySpecification =
       LegacyDesugaredLibrarySpecification.empty();
 
+  public PrefixRewritingMapper getPrefixRewritingMapper() {
+    if (testing.machineDesugaredLibrarySpecification != null) {
+      return new MachineDesugarPrefixRewritingMapper(
+          desugaredLibrarySpecification.getPrefixRewritingMapper(),
+          testing.machineDesugaredLibrarySpecification.getRewritingFlags());
+    }
+    return desugaredLibrarySpecification.getPrefixRewritingMapper();
+  }
+
   public boolean relocatorCompilation = false;
 
   // If null, no keep rules are recorded.