Generate DesugarMethodHandlesLookup class

Only supporting findVarHandle.

Bug: b/247076137
Change-Id: I14866c98481588ae0717a82655a500f86295dc36
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/disabledesugarer/DesugaredLibraryDisableDesugarer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/disabledesugarer/DesugaredLibraryDisableDesugarer.java
index bd4b771..5c956aa 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/disabledesugarer/DesugaredLibraryDisableDesugarer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/disabledesugarer/DesugaredLibraryDisableDesugarer.java
@@ -62,6 +62,7 @@
     return rewriteInstruction(instruction, context) != null;
   }
 
+  // TODO(b/261024278): Share this code.
   private CfInstruction rewriteInstruction(CfInstruction instruction, ProgramMethod context) {
     if (!appView.dexItemFactory().multiDexTypes.contains(context.getHolderType())) {
       return null;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringMethods.java
index d463636..ae46d63 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringMethods.java
@@ -18,8 +18,10 @@
 import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.cf.code.CfLabel;
 import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfNew;
 import com.android.tools.r8.cf.code.CfReturn;
 import com.android.tools.r8.cf.code.CfReturnVoid;
+import com.android.tools.r8.cf.code.CfStackInstruction;
 import com.android.tools.r8.cf.code.CfStore;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.CfCode;
@@ -222,6 +224,45 @@
                 .build()));
   }
 
+  public static void generateDesugarMethodHandlesLookupClass(
+      SyntheticProgramClassBuilder builder, DexItemFactory factory) {
+    builder.setInstanceFields(ImmutableList.of());
+    DexMethod findVarHandle =
+        factory.createMethod(
+            builder.getType(),
+            factory.createProto(
+                factory.createType(factory.createString("Lcom/android/tools/r8/DesugarVarHandle;")),
+                factory.createType(factory.createString("Ljava/lang/Class;")),
+                factory.createType(factory.createString("Ljava/lang/String;")),
+                factory.createType(factory.createString("Ljava/lang/Class;"))),
+            factory.createString("findVarHandle"));
+    DexMethod constructor_0 =
+        factory.createMethod(
+            builder.getType(),
+            factory.createProto(factory.voidType),
+            factory.createString("<init>"));
+    builder.setDirectMethods(
+        ImmutableList.of(
+            DexEncodedMethod.syntheticBuilder()
+                .setMethod(constructor_0)
+                .setAccessFlags(
+                    MethodAccessFlags.fromSharedAccessFlags(
+                        Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC, true))
+                .setCode(DesugarMethodHandlesLookup_constructor_0(factory, constructor_0))
+                .disableAndroidApiLevelCheck()
+                .build()));
+    builder.setVirtualMethods(
+        ImmutableList.of(
+            DexEncodedMethod.syntheticBuilder()
+                .setMethod(findVarHandle)
+                .setAccessFlags(
+                    MethodAccessFlags.fromSharedAccessFlags(
+                        Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC, false))
+                .setCode(DesugarMethodHandlesLookup_findVarHandle(factory, findVarHandle))
+                .disableAndroidApiLevelCheck()
+                .build()));
+  }
+
   public static CfCode DesugarVarHandle_constructor_3(DexItemFactory factory, DexMethod method) {
     CfLabel label0 = new CfLabel();
     CfLabel label1 = new CfLabel();
@@ -472,4 +513,57 @@
         ImmutableList.of(),
         ImmutableList.of());
   }
+
+  public static CfCode DesugarMethodHandlesLookup_constructor_0(
+      DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        1,
+        1,
+        ImmutableList.of(
+            label0,
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfInvoke(
+                183,
+                factory.createMethod(
+                    factory.objectType,
+                    factory.createProto(factory.voidType),
+                    factory.createString("<init>")),
+                false),
+            new CfReturnVoid(),
+            label1),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
+
+  public static CfCode DesugarMethodHandlesLookup_findVarHandle(
+      DexItemFactory factory, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        5,
+        4,
+        ImmutableList.of(
+            label0,
+            new CfNew(factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+            new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.OBJECT, 2),
+            new CfLoad(ValueType.OBJECT, 3),
+            new CfInvoke(
+                183,
+                factory.createMethod(
+                    factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+                    factory.createProto(
+                        factory.voidType, factory.classType, factory.stringType, factory.classType),
+                    factory.createString("<init>")),
+                false),
+            new CfReturn(ValueType.OBJECT),
+            label1),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/varhandle/DesugarMethodHandlesLookup.java b/src/test/java/com/android/tools/r8/ir/desugar/varhandle/DesugarMethodHandlesLookup.java
new file mode 100644
index 0000000..d700033
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/varhandle/DesugarMethodHandlesLookup.java
@@ -0,0 +1,126 @@
+// 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.varhandle;
+
+public final class DesugarMethodHandlesLookup {
+  public DesugarVarHandle findVarHandle(Class<?> recv, String name, Class<?> type)
+      throws NoSuchFieldException, IllegalAccessException {
+    return new DesugarVarHandle(recv, name, type);
+  }
+
+  /*
+   * Remaining methods on MethodHandles.Lookup.
+   *
+   * These could be implemented by forwarding to the runtime MethodHandles.Lookup if present at
+   * runtime.
+   *
+
+  public Class<?>	accessClass(Class<?> targetClass) throws IllegalAccessException {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public MethodHandle bind(Object receiver, String name, MethodType type) throws NoSuchMethodException,
+      IllegalAccessException {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public Class<?> defineClass(byte[] bytes) throws IllegalAccessException {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public MethodHandles.Lookup dropLookupMode(int modeToDrop) {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public Class<?> findClass(String targetName) throws ClassNotFoundException, IllegalAccessException {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public MethodHandle findConstructor(Class<?> refc, MethodType type) throws NoSuchMethodException, IllegalAccessException {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public MethodHandle findGetter(Class<?> refc, String name, Class<?> type) throws NoSuchMethodException, IllegalAccessException {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public MethodHandle findSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public MethodHandle findSpecial(Class<?> refc, String name, MethodType type, Class<?> specialCaller) throws NoSuchFieldException, IllegalAccessException {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public MethodHandle findStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public MethodHandle findStaticGetter(Class<?> refc, String name, Class<?> type) throws NoSuchMethodException, IllegalAccessException {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public MethodHandle findStaticSetter(Class<?> refc, String name, Class<?> type) throws NoSuchMethodException, IllegalAccessException {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public DesugarVarHandle findStaticVarHandle(Class<?> decl, String name, Class<?> type) throws Exception throws NoSuchFieldException, IllegalAccessException {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public MethodHandle findVirtual(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public boolean hasPrivateAccess() {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public MethodHandles.Lookup in(Class<?> requestedLookupClass) {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public Class<?> lookupClass() {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public int lookupModes() {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public MethodHandleInfo revealDirect(MethodHandle target) {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public String toString() {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public MethodHandle unreflect(Method m) throws IllegalAccessException {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public MethodHandle unreflectConstructor(Constructor<?> c) throws IllegalAccessException {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public MethodHandle unreflectGetter(Field f) throws IllegalAccessException {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public MethodHandle unreflectSetter(Field f) throws IllegalAccessException {
+    throw new RuntimeException("Unsupported");
+  }
+
+  public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) {
+    throw new RuntimeException("Unsupported");
+  }
+
+  DesugarVarHandle unreflectVarHandle(Field f) throws IllegalAccessException {
+    throw new RuntimeException("Unsupported");
+  }
+
+  */
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/varhandle/DesugarVarHandle.java b/src/test/java/com/android/tools/r8/ir/desugar/varhandle/DesugarVarHandle.java
index 2a19a45..7e88943 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/varhandle/DesugarVarHandle.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/varhandle/DesugarVarHandle.java
@@ -22,7 +22,8 @@
   private final Class<?> type;
   private final long offset;
 
-  DesugarVarHandle(Class<?> recv, String name, Class<?> type) throws Exception {
+  DesugarVarHandle(Class<?> recv, String name, Class<?> type)
+      throws NoSuchFieldException, IllegalAccessException {
     Field theUnsafe = UnsafeStub.class.getDeclaredField("theUnsafe");
     theUnsafe.setAccessible(true);
     U = (UnsafeStub) theUnsafe.get(null);
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/varhandle/GenerateVarHandleMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/varhandle/GenerateVarHandleMethods.java
index b15131b..a8527a0 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/varhandle/GenerateVarHandleMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/varhandle/GenerateVarHandleMethods.java
@@ -10,22 +10,25 @@
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.cf.code.CfFieldInstruction;
+import com.android.tools.r8.cf.code.CfFrame;
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfTypeInstruction;
 import com.android.tools.r8.cfmethodgeneration.MethodGenerationBase;
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.utils.FileUtils;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -37,7 +40,8 @@
 
   private final DexType GENERATED_TYPE =
       factory.createType("Lcom/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringMethods;");
-  private final List<Class<?>> METHOD_TEMPLATE_CLASSES = ImmutableList.of(DesugarVarHandle.class);
+  private final List<Class<?>> METHOD_TEMPLATE_CLASSES =
+      ImmutableList.of(DesugarVarHandle.class, DesugarMethodHandlesLookup.class);
 
   protected final TestParameters parameters;
 
@@ -62,7 +66,7 @@
 
   @Override
   protected List<Class<?>> getClassesToGenerate() {
-    return ImmutableList.of(DesugarVarHandle.class);
+    return ImmutableList.of(DesugarVarHandle.class, DesugarMethodHandlesLookup.class);
   }
 
   @Override
@@ -76,63 +80,6 @@
     return 2022;
   }
 
-  private static CfInstruction rewriteToUnsafe(DexItemFactory factory, CfInstruction instruction) {
-    DexType unsafe = factory.unsafeType;
-    DexType unsafeStub =
-        factory.createType(
-            "L" + DesugarVarHandle.class.getTypeName().replace('.', '/') + "$UnsafeStub;");
-    // Rewrite references to UnsafeStub to sun.misc.Unsafe
-    if (instruction.isTypeInstruction()
-        && instruction.asTypeInstruction().getType() == unsafeStub) {
-      return instruction.asTypeInstruction().withType(unsafe);
-    }
-    if (instruction.isFieldInstruction()) {
-      CfFieldInstruction fieldInstruction = instruction.asFieldInstruction();
-      if (fieldInstruction.getField().getType() == unsafeStub) {
-        return fieldInstruction.createWithField(
-            factory.createField(
-                fieldInstruction.getField().getHolderType(),
-                unsafe,
-                fieldInstruction.getField().name));
-      }
-    }
-    if (instruction.isInvoke()) {
-      CfInvoke invoke = instruction.asInvoke();
-      DexMethod method = invoke.getMethod();
-      String name = method.getName().toString();
-      if (invoke.getMethod().getHolderType() == unsafeStub) {
-        return new CfInvoke(
-            invoke.getOpcode(),
-            factory.createMethod(unsafe, invoke.getMethod().getProto(), factory.createString(name)),
-            invoke.isInterface());
-      }
-    }
-    if (instruction.isFrame()) {
-      return instruction.asFrame().mapReferenceTypes(type -> (type == unsafeStub) ? unsafe : type);
-    }
-    return instruction;
-  }
-
-  private static CfInstruction rewriteDesugarVarHandle(
-      DexItemFactory factory, CfInstruction instruction) {
-    // Rewrite references to com.android.tools.r8.ir.desugar.varhandle.DesugarVarHandle to
-    // com.android.tools.r8.DesugarVarHandle.
-    DexType desugarVarHandle = factory.desugarVarHandleType;
-    DexType desugarVarHandleStub =
-        factory.createType("L" + DesugarVarHandle.class.getTypeName().replace('.', '/') + ";");
-    if (instruction.isFieldInstruction()) {
-      CfFieldInstruction fieldInstruction = instruction.asFieldInstruction();
-      if (fieldInstruction.getField().getHolderType() == desugarVarHandleStub) {
-        return fieldInstruction.createWithField(
-            factory.createField(
-                desugarVarHandle,
-                fieldInstruction.getField().getType(),
-                fieldInstruction.getField().name));
-      }
-    }
-    return instruction;
-  }
-
   @Override
   protected DexEncodedField getField(DexEncodedField field) {
     if (field.getType().getTypeName().endsWith("$UnsafeStub")) {
@@ -146,19 +93,95 @@
     return field;
   }
 
+  // TODO(b/261024278): Share this code.
+  private class InstructionTypeMapper {
+    private final Map<DexType, DexType> typeMap;
+
+    InstructionTypeMapper(Map<DexType, DexType> typeMap) {
+      this.typeMap = typeMap;
+    }
+
+    private CfInstruction rewriteInstruction(CfInstruction instruction) {
+      if (instruction.isTypeInstruction()) {
+        CfInstruction rewritten = rewriteTypeInstruction(instruction.asTypeInstruction());
+        return rewritten == null ? instruction : rewritten;
+      }
+      if (instruction.isFieldInstruction()) {
+        return rewriteFieldInstruction(instruction.asFieldInstruction());
+      }
+      if (instruction.isInvoke()) {
+        return rewriteInvokeInstruction(instruction.asInvoke());
+      }
+      if (instruction.isFrame()) {
+        return rewriteFrameInstruction(instruction.asFrame());
+      }
+      return instruction;
+    }
+
+    private CfInstruction rewriteInvokeInstruction(CfInvoke instruction) {
+      CfInvoke invoke = instruction.asInvoke();
+      DexMethod method = invoke.getMethod();
+      String name = method.getName().toString();
+      DexType holderType = invoke.getMethod().getHolderType();
+      DexType rewrittenType = typeMap.getOrDefault(holderType, holderType);
+      if (rewrittenType != holderType) {
+        // TODO(b/261024278): If sharing this code also rewrite signature.
+        return new CfInvoke(
+            invoke.getOpcode(),
+            factory.createMethod(
+                rewrittenType, invoke.getMethod().getProto(), factory.createString(name)),
+            invoke.isInterface());
+      }
+      return instruction;
+    }
+
+    private CfFieldInstruction rewriteFieldInstruction(CfFieldInstruction instruction) {
+      DexType holderType = instruction.getField().getHolderType();
+      DexType rewrittenHolderType = typeMap.getOrDefault(holderType, holderType);
+      DexType fieldType = instruction.getField().getType();
+      DexType rewrittenType = typeMap.getOrDefault(fieldType, fieldType);
+      if (rewrittenHolderType != holderType || rewrittenType != fieldType) {
+        return instruction.createWithField(
+            factory.createField(rewrittenHolderType, rewrittenType, instruction.getField().name));
+      }
+      return instruction;
+    }
+
+    private CfInstruction rewriteTypeInstruction(CfTypeInstruction instruction) {
+      DexType rewrittenType = typeMap.getOrDefault(instruction.getType(), instruction.getType());
+      return rewrittenType != instruction.getType() ? instruction.withType(rewrittenType) : null;
+    }
+
+    private CfInstruction rewriteFrameInstruction(CfFrame instruction) {
+      return instruction.asFrame().mapReferenceTypes(type -> typeMap.getOrDefault(type, type));
+    }
+  }
+
   @Override
   protected CfCode getCode(String holderName, String methodName, CfCode code) {
     if (methodName.endsWith("Stub")) {
       // Don't include stubs targeted only for rewriting in the generated code.
       return null;
     }
-    if (!holderName.equals("DesugarVarHandle")) {
-      throw new RuntimeException("Unexpected");
+    if (!holderName.equals("DesugarVarHandle")
+        && !holderName.equals("DesugarMethodHandlesLookup")) {
+      throw new RuntimeException("Unexpected: " + holderName);
     }
+    // Rewrite references to com.android.tools.r8.ir.desugar.varhandle.DesugarVarHandle to
+    // com.android.tools.r8.DesugarVarHandle and rewrite references to UnsafeStub to
+    // sun.misc.Unsafe.
+    InstructionTypeMapper instructionTypeMapper =
+        new InstructionTypeMapper(
+            ImmutableMap.of(
+                factory.createType(
+                    "L" + DesugarVarHandle.class.getTypeName().replace('.', '/') + ";"),
+                factory.desugarVarHandleType,
+                factory.createType(
+                    "L" + DesugarVarHandle.class.getTypeName().replace('.', '/') + "$UnsafeStub;"),
+                factory.unsafeType));
     code.setInstructions(
         code.getInstructions().stream()
-            .map(instruction -> rewriteToUnsafe(factory, instruction))
-            .map(instruction -> rewriteDesugarVarHandle(factory, instruction))
+            .map(instructionTypeMapper::rewriteInstruction)
             .collect(Collectors.toList()));
     return code;
   }