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;
}