Introduce Machine specifications

Bug: 184026720
Change-Id: I482121268cb0e22313d8c62d2126173b396b813f
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
new file mode 100644
index 0000000..aac8c01
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
@@ -0,0 +1,18 @@
+// 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.machinespecification;
+
+/** TODO(b/184026720): Move the rest of the flags. */
+public class MachineDesugaredLibrarySpecification {
+
+  private final boolean libraryCompilation;
+  private final MachineRewritingFlags rewritingFlags;
+
+  public MachineDesugaredLibrarySpecification(
+      boolean libraryCompilation, MachineRewritingFlags rewritingFlags) {
+    this.libraryCompilation = libraryCompilation;
+    this.rewritingFlags = rewritingFlags;
+  }
+}
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
new file mode 100644
index 0000000..4dd87f6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
@@ -0,0 +1,56 @@
+// 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.machinespecification;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+
+public class MachineRewritingFlags {
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  MachineRewritingFlags(
+      Map<DexMethod, DexMethod> staticRetarget,
+      Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget) {
+    this.staticRetarget = staticRetarget;
+    this.nonEmulatedVirtualRetarget = nonEmulatedVirtualRetarget;
+  }
+
+  // Static methods to retarget, duplicated to library boundaries.
+  private final Map<DexMethod, DexMethod> staticRetarget;
+
+  // Virtual methods to retarget, which are guaranteed not to require emulated dispatch.
+  // A method does not require emulated dispatch if two conditions are met:
+  // (1) the method does not override any other library method;
+  // (2) the method is final or installed in a final class.
+  // Any invoke resolving into the method will be rewritten into an invoke-static to the desugared
+  // code.
+  private final Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget;
+
+  public static class Builder {
+
+    Builder() {}
+
+    private final ImmutableMap.Builder<DexMethod, DexMethod> staticRetarget =
+        ImmutableMap.builder();
+    private final ImmutableMap.Builder<DexMethod, DexMethod> nonEmulatedVirtualRetarget =
+        ImmutableMap.builder();
+
+    public void putStaticRetarget(DexMethod src, DexMethod dest) {
+      staticRetarget.put(src, dest);
+    }
+
+    public void putNonEmulatedVirtualRetarget(DexMethod src, DexMethod dest) {
+      nonEmulatedVirtualRetarget.put(src, dest);
+    }
+
+    public MachineRewritingFlags build() {
+      return new MachineRewritingFlags(staticRetarget.build(), nonEmulatedVirtualRetarget.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
new file mode 100644
index 0000000..7796c19
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
@@ -0,0 +1,139 @@
+// 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.dex.ApplicationReader;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodResolutionResult;
+import com.android.tools.r8.graph.SubtypingInfo;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.concurrent.ExecutorService;
+import java.util.function.BiConsumer;
+
+public class HumanToMachineSpecificationConverter {
+
+  public MachineDesugaredLibrarySpecification convert(
+      HumanDesugaredLibrarySpecification humanSpec, Path androidLib, InternalOptions options)
+      throws IOException {
+    DexApplication app = readApp(androidLib, options);
+    AppView<?> appView = AppView.createForD8(AppInfo.createInitialAppInfo(app));
+    MachineRewritingFlags machineRewritingFlags =
+        convertRewritingFlags(humanSpec.getRewritingFlags(), appView.appInfoForDesugaring());
+    return new MachineDesugaredLibrarySpecification(
+        humanSpec.isLibraryCompilation(), machineRewritingFlags);
+  }
+
+  private MachineRewritingFlags convertRewritingFlags(
+      HumanRewritingFlags rewritingFlags, AppInfoWithClassHierarchy appInfo) {
+    MachineRewritingFlags.Builder builder = MachineRewritingFlags.builder();
+    SubtypingInfo subtypingInfo = new SubtypingInfo(appInfo);
+    rewritingFlags
+        .getRetargetCoreLibMember()
+        .forEach(
+            (method, type) ->
+                convertRetargetCoreLibMemberFlag(builder, method, type, appInfo, subtypingInfo));
+    return builder.build();
+  }
+
+  private void convertRetargetCoreLibMemberFlag(
+      MachineRewritingFlags.Builder builder,
+      DexMethod method,
+      DexType type,
+      AppInfoWithClassHierarchy appInfo,
+      SubtypingInfo subtypingInfo) {
+    DexClass holder = appInfo.definitionFor(method.holder);
+    DexEncodedMethod foundMethod = holder.lookupMethod(method);
+    assert foundMethod != null;
+    if (foundMethod.isStatic()) {
+      convertStaticRetarget(builder, foundMethod, type, appInfo, subtypingInfo);
+      return;
+    }
+    if (holder.isFinal() || foundMethod.isFinal()) {
+      convertNonEmulatedVirtualRetarget(builder, foundMethod, type, appInfo, subtypingInfo);
+      return;
+    }
+    convertEmulatedVirtualRetarget(builder, foundMethod, type, appInfo, subtypingInfo);
+  }
+
+  private void convertEmulatedVirtualRetarget(
+      MachineRewritingFlags.Builder builder,
+      DexEncodedMethod foundMethod,
+      DexType type,
+      AppInfoWithClassHierarchy appInfo,
+      SubtypingInfo subtypingInfo) {
+    // TODO(b/184026720): To implement.
+  }
+
+  private void convertNonEmulatedRetarget(
+      DexEncodedMethod foundMethod,
+      DexType type,
+      AppInfoWithClassHierarchy appInfo,
+      SubtypingInfo subtypingInfo,
+      BiConsumer<DexMethod, DexMethod> consumer) {
+    DexMethod src = foundMethod.getReference();
+    DexMethod dest = src.withHolder(type, appInfo.dexItemFactory());
+    consumer.accept(src, dest);
+    for (DexType subtype : subtypingInfo.subtypes(foundMethod.getHolderType())) {
+      DexClass subclass = appInfo.definitionFor(subtype);
+      MethodResolutionResult resolutionResult = appInfo.resolveMethodOn(subclass, src);
+      if (resolutionResult.isSuccessfulMemberResolutionResult()
+          && resolutionResult.getResolvedMethod().getReference() == src) {
+        consumer.accept(src.withHolder(subtype, appInfo.dexItemFactory()), dest);
+      }
+    }
+  }
+
+  private void convertNonEmulatedVirtualRetarget(
+      MachineRewritingFlags.Builder builder,
+      DexEncodedMethod foundMethod,
+      DexType type,
+      AppInfoWithClassHierarchy appInfo,
+      SubtypingInfo subtypingInfo) {
+    convertNonEmulatedRetarget(
+        foundMethod,
+        type,
+        appInfo,
+        subtypingInfo,
+        (src, dest) ->
+            builder.putNonEmulatedVirtualRetarget(
+                src,
+                dest.withExtraArgumentPrepended(
+                    foundMethod.getHolderType(), appInfo.dexItemFactory())));
+  }
+
+  private void convertStaticRetarget(
+      MachineRewritingFlags.Builder builder,
+      DexEncodedMethod foundMethod,
+      DexType type,
+      AppInfoWithClassHierarchy appInfo,
+      SubtypingInfo subtypingInfo) {
+    convertNonEmulatedRetarget(
+        foundMethod, type, appInfo, subtypingInfo, builder::putStaticRetarget);
+  }
+
+  private DexApplication readApp(Path androidLib, InternalOptions options) throws IOException {
+    AndroidApp androidApp = AndroidApp.builder().addProgramFile(androidLib).build();
+    ApplicationReader applicationReader =
+        new ApplicationReader(androidApp, options, Timing.empty());
+    ExecutorService executorService = ThreadUtils.getExecutorService(options);
+    return applicationReader.read(executorService).toDirect();
+  }
+}
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 14898fc..34bf5fa 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
@@ -15,7 +15,6 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DirectMappedDexApplication;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanTopLevelFlags;
@@ -43,9 +42,8 @@
 import java.util.Map;
 import java.util.concurrent.ExecutorService;
 
-public class LegacyToHumanSpecificationConverter implements SpecificationConverter {
+public class LegacyToHumanSpecificationConverter {
 
-  @Override
   public void convertAllAPILevels(
       StringResource inputSpecification, Path androidLib, StringConsumer output)
       throws IOException {
@@ -136,8 +134,7 @@
     libraryFlags.put(level, builder.build());
   }
 
-  private DirectMappedDexApplication readApp(Path androidLib, InternalOptions options)
-      throws IOException {
+  private DexApplication readApp(Path androidLib, InternalOptions options) throws IOException {
     AndroidApp androidApp = AndroidApp.builder().addLibraryFile(androidLib).build();
     ApplicationReader applicationReader =
         new ApplicationReader(androidApp, options, Timing.empty());
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/SpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/SpecificationConverter.java
deleted file mode 100644
index 85a8c8b..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/SpecificationConverter.java
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) 2021, 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.StringConsumer;
-import com.android.tools.r8.StringResource;
-import java.io.IOException;
-import java.nio.file.Path;
-
-public interface SpecificationConverter {
-
-  void convertAllAPILevels(
-      StringResource inputSpecification, Path androidLib, StringConsumer output) throws IOException;
-}