Machine Specification for emulated interface
Bug: 184026720
Change-Id: I4b522634dc16cdd35c79645cf971afda60bb2ed9
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedDispatchMethodDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedDispatchMethodDescriptor.java
index c5154cd..6eb2424 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedDispatchMethodDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedDispatchMethodDescriptor.java
@@ -37,7 +37,6 @@
* method is the method on the companion class.
*/
private final DerivedMethod interfaceMethod;
-
private final DerivedMethod emulatedDispatchMethod;
private final DerivedMethod forwardingMethod;
private final LinkedHashMap<DexType, DerivedMethod> dispatchCases;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedInterfaceDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedInterfaceDescriptor.java
new file mode 100644
index 0000000..0f9a520
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedInterfaceDescriptor.java
@@ -0,0 +1,28 @@
+// 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.android.tools.r8.graph.DexType;
+import java.util.Map;
+
+public class EmulatedInterfaceDescriptor {
+ private final DexType rewrittenType;
+ private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedMethods;
+
+ public EmulatedInterfaceDescriptor(
+ DexType rewrittenType, Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedMethods) {
+ this.rewrittenType = rewrittenType;
+ this.emulatedMethods = emulatedMethods;
+ }
+
+ public DexType getRewrittenType() {
+ return rewrittenType;
+ }
+
+ public Map<DexMethod, EmulatedDispatchMethodDescriptor> getEmulatedMethods() {
+ return emulatedMethods;
+ }
+}
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 f73e1c9..db17cf5 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
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
@@ -17,10 +18,12 @@
MachineRewritingFlags(
Map<DexMethod, DexMethod> staticRetarget,
Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget,
- Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget) {
+ Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget,
+ Map<DexType, EmulatedInterfaceDescriptor> emulatedInterfaces) {
this.staticRetarget = staticRetarget;
this.nonEmulatedVirtualRetarget = nonEmulatedVirtualRetarget;
this.emulatedVirtualRetarget = emulatedVirtualRetarget;
+ this.emulatedInterfaces = emulatedInterfaces;
}
// Static methods to retarget, duplicated to library boundaries.
@@ -37,6 +40,9 @@
// Virtual methods to retarget through emulated dispatch.
private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget;
+ // Emulated interface descriptors.
+ private final Map<DexType, EmulatedInterfaceDescriptor> emulatedInterfaces;
+
public Map<DexMethod, DexMethod> getStaticRetarget() {
return staticRetarget;
}
@@ -49,6 +55,10 @@
return emulatedVirtualRetarget;
}
+ public Map<DexType, EmulatedInterfaceDescriptor> getEmulatedInterfaces() {
+ return emulatedInterfaces;
+ }
+
public static class Builder {
Builder() {}
@@ -59,6 +69,8 @@
ImmutableMap.builder();
private final ImmutableMap.Builder<DexMethod, EmulatedDispatchMethodDescriptor>
emulatedVirtualRetarget = ImmutableMap.builder();
+ private final ImmutableMap.Builder<DexType, EmulatedInterfaceDescriptor> emulatedInterfaces =
+ ImmutableMap.builder();
public void putStaticRetarget(DexMethod src, DexMethod dest) {
staticRetarget.put(src, dest);
@@ -68,6 +80,10 @@
nonEmulatedVirtualRetarget.put(src, dest);
}
+ public void putEmulatedInterface(DexType src, EmulatedInterfaceDescriptor descriptor) {
+ emulatedInterfaces.put(src, descriptor);
+ }
+
public void putEmulatedVirtualRetarget(DexMethod src, EmulatedDispatchMethodDescriptor dest) {
emulatedVirtualRetarget.put(src, dest);
}
@@ -76,7 +92,8 @@
return new MachineRewritingFlags(
staticRetarget.build(),
nonEmulatedVirtualRetarget.build(),
- emulatedVirtualRetarget.build());
+ emulatedVirtualRetarget.build(),
+ emulatedInterfaces.build());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java
index 5a5768f..8f7d25f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.ClasspathOrLibraryClass;
import com.android.tools.r8.graph.DexClass;
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.graph.MethodAccessFlags;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
@@ -37,7 +38,8 @@
private DexMethod emulatedHolderDispatchMethod(DexType holder, DerivedMethod method) {
assert method.getHolderKind() == SyntheticKind.RETARGET_CLASS;
- return appView.dexItemFactory().createMethod(holder, method.getProto(), method.getName());
+ DexProto newProto = appView.dexItemFactory().prependHolderToProto(method.getMethod());
+ return appView.dexItemFactory().createMethod(holder, newProto, method.getName());
}
DexMethod emulatedInterfaceDispatchMethod(DexType holder, DerivedMethod method) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/RetargetingInfo.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/RetargetingInfo.java
index eb38e9e..9e1f5e3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/RetargetingInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/RetargetingInfo.java
@@ -115,12 +115,8 @@
DerivedMethod forwardingMethod = new DerivedMethod(forwardingDexMethod);
DerivedMethod interfaceMethod =
new DerivedMethod(methodReference, SyntheticKind.RETARGET_INTERFACE);
- DexMethod dispatchDexMethod =
- appView
- .dexItemFactory()
- .createMethod(methodReference.getHolderType(), newProto, methodName);
DerivedMethod dispatchMethod =
- new DerivedMethod(dispatchDexMethod, SyntheticKind.RETARGET_CLASS);
+ new DerivedMethod(methodReference, SyntheticKind.RETARGET_CLASS);
emulatedVirtualRetarget.put(
methodReference,
new EmulatedDispatchMethodDescriptor(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java
new file mode 100644
index 0000000..2411ba0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java
@@ -0,0 +1,183 @@
+// 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.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProto;
+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.DerivedMethod;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedInterfaceDescriptor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.WorkList;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class HumanToMachineEmulatedInterfaceConverter {
+
+ private final AppInfoWithClassHierarchy appInfo;
+ private Map<DexType, List<DexType>> emulatedInterfaceHierarchy;
+
+ public HumanToMachineEmulatedInterfaceConverter(AppInfoWithClassHierarchy appInfo) {
+ this.appInfo = appInfo;
+ }
+
+ public void convertEmulatedInterfaces(
+ HumanRewritingFlags rewritingFlags,
+ AppInfoWithClassHierarchy appInfo,
+ MachineRewritingFlags.Builder builder) {
+ Map<DexType, DexType> emulateInterfaces = rewritingFlags.getEmulateLibraryInterface();
+ Set<DexMethod> dontRewriteInvocation = rewritingFlags.getDontRewriteInvocation();
+ emulatedInterfaceHierarchy = processEmulatedInterfaceHierarchy(appInfo, emulateInterfaces);
+ for (DexType itf : emulateInterfaces.keySet()) {
+ DexProgramClass itfClass = appInfo.contextIndependentDefinitionFor(itf).asProgramClass();
+ assert itfClass != null;
+ Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedMethods = new IdentityHashMap<>();
+ itfClass.forEachProgramVirtualMethodMatching(
+ m -> m.isDefaultMethod() && !dontRewriteInvocation.contains(m.getReference()),
+ method ->
+ emulatedMethods.put(
+ method.getReference(),
+ computeEmulatedDispatchDescriptor(
+ method.getReference(), rewritingFlags, appInfo)));
+ builder.putEmulatedInterface(
+ itf, new EmulatedInterfaceDescriptor(emulateInterfaces.get(itf), emulatedMethods));
+ }
+ }
+
+ private EmulatedDispatchMethodDescriptor computeEmulatedDispatchDescriptor(
+ DexMethod method, HumanRewritingFlags rewritingFlags, AppInfoWithClassHierarchy appInfo) {
+ DerivedMethod forwardingMethod = new DerivedMethod(method, SyntheticKind.COMPANION_CLASS);
+ DexMethod itfDexMethod =
+ appInfo
+ .dexItemFactory()
+ .createMethod(
+ rewritingFlags.getEmulateLibraryInterface().get(method.getHolderType()),
+ method.getProto(),
+ method.getName());
+ DerivedMethod interfaceMethod = new DerivedMethod(itfDexMethod);
+ DerivedMethod dispatchMethod =
+ new DerivedMethod(method, SyntheticKind.EMULATED_INTERFACE_CLASS);
+ LinkedHashMap<DexType, DerivedMethod> dispatchCases = getDispatchCases(rewritingFlags, method);
+ return new EmulatedDispatchMethodDescriptor(
+ interfaceMethod, dispatchMethod, forwardingMethod, dispatchCases);
+ }
+
+ private LinkedHashMap<DexType, DerivedMethod> getDispatchCases(
+ HumanRewritingFlags rewritingFlags, DexMethod method) {
+ // To properly emulate the library interface call, we need to compute the interfaces
+ // inheriting from the interface and manually implement the dispatch with instance of.
+ // The list guarantees that an interface is always after interfaces it extends,
+ // hence reverse iteration.
+ List<DexType> subInterfaces = emulatedInterfaceHierarchy.get(method.getHolderType());
+ LinkedHashMap<DexType, DerivedMethod> extraDispatchCases = new LinkedHashMap<>();
+ // Retarget core lib emulated dispatch handled as part of emulated interface dispatch.
+ Map<DexMethod, DexType> retargetCoreLibMember = rewritingFlags.getRetargetCoreLibMember();
+ for (DexMethod retarget : retargetCoreLibMember.keySet()) {
+ if (retarget.match(method)) {
+ DexClass inClass = appInfo.definitionFor(retarget.getHolderType());
+ if (inClass != null && implementsInterface(inClass, method.getHolderType())) {
+ DexProto newProto = appInfo.dexItemFactory().prependHolderToProto(retarget);
+ DexMethod forwardingDexMethod =
+ appInfo
+ .dexItemFactory()
+ .createMethod(retargetCoreLibMember.get(retarget), newProto, retarget.getName());
+ extraDispatchCases.put(retarget.getHolderType(), new DerivedMethod(forwardingDexMethod));
+ }
+ }
+ }
+ if (subInterfaces != null) {
+ for (int i = subInterfaces.size() - 1; i >= 0; i--) {
+ DexClass subInterfaceClass = appInfo.definitionFor(subInterfaces.get(i));
+ assert subInterfaceClass != null;
+ assert subInterfaceClass.isProgramClass();
+ // Else computation of subInterface would have failed.
+ // if the method is implemented, extra dispatch is required.
+ DexEncodedMethod result = subInterfaceClass.lookupVirtualMethod(method);
+ if (result != null && !result.isAbstract()) {
+ assert result.isDefaultMethod();
+ DexMethod reference = result.getReference();
+ extraDispatchCases.put(
+ subInterfaceClass.type, new DerivedMethod(reference, SyntheticKind.COMPANION_CLASS));
+ }
+ }
+ } else {
+ assert extraDispatchCases.size() <= 1;
+ }
+ return extraDispatchCases;
+ }
+
+ private boolean implementsInterface(DexClass clazz, DexType interfaceType) {
+ WorkList<DexType> workList =
+ WorkList.newIdentityWorkList(Arrays.asList(clazz.interfaces.values));
+ while (!workList.isEmpty()) {
+ DexType next = workList.next();
+ if (interfaceType == next) {
+ return true;
+ }
+ DexClass nextClass = appInfo.definitionFor(next);
+ if (nextClass != null) {
+ workList.addIfNotSeen(nextClass.interfaces.values);
+ }
+ }
+ return false;
+ }
+
+ private Map<DexType, List<DexType>> processEmulatedInterfaceHierarchy(
+ AppInfoWithClassHierarchy appInfo, Map<DexType, DexType> emulateInterfaces) {
+ Map<DexType, List<DexType>> emulatedInterfacesHierarchy = new IdentityHashMap<>();
+ Set<DexType> processed = Sets.newIdentityHashSet();
+ ArrayList<DexType> emulatedInterfacesSorted = new ArrayList<>(emulateInterfaces.keySet());
+ emulatedInterfacesSorted.sort(DexType::compareTo);
+ for (DexType interfaceType : emulatedInterfacesSorted) {
+ processEmulatedInterfaceHierarchy(
+ appInfo, emulateInterfaces, interfaceType, processed, emulatedInterfacesHierarchy);
+ }
+ return emulatedInterfacesHierarchy;
+ }
+
+ private void processEmulatedInterfaceHierarchy(
+ AppInfoWithClassHierarchy appInfo,
+ Map<DexType, DexType> emulateInterfaces,
+ DexType interfaceType,
+ Set<DexType> processed,
+ Map<DexType, List<DexType>> emulatedInterfacesHierarchy) {
+ if (processed.contains(interfaceType)) {
+ return;
+ }
+ emulatedInterfacesHierarchy.put(interfaceType, new ArrayList<>());
+ processed.add(interfaceType);
+ DexClass theInterface = appInfo.definitionFor(interfaceType);
+ if (theInterface == null) {
+ return;
+ }
+ WorkList<DexType> workList =
+ WorkList.newIdentityWorkList(Arrays.asList(theInterface.interfaces.values));
+ while (!workList.isEmpty()) {
+ DexType next = workList.next();
+ if (emulateInterfaces.containsKey(next)) {
+ processEmulatedInterfaceHierarchy(
+ appInfo, emulateInterfaces, next, processed, emulatedInterfacesHierarchy);
+ emulatedInterfacesHierarchy.get(next).add(interfaceType);
+ DexClass nextClass = appInfo.definitionFor(next);
+ if (nextClass != null) {
+ workList.addIfNotSeen(nextClass.interfaces.values);
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
new file mode 100644
index 0000000..fae5b31
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
@@ -0,0 +1,168 @@
+// 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.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+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.graph.MethodResolutionResult;
+import com.android.tools.r8.graph.SubtypingInfo;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.TraversalContinuation;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+public class HumanToMachineRetargetConverter {
+
+ private final AppInfoWithClassHierarchy appInfo;
+ private SubtypingInfo subtypingInfo;
+
+ public HumanToMachineRetargetConverter(AppInfoWithClassHierarchy appInfo) {
+ this.appInfo = appInfo;
+ }
+
+ public void convertRetargetFlags(
+ HumanRewritingFlags rewritingFlags, MachineRewritingFlags.Builder builder) {
+ subtypingInfo = new SubtypingInfo(appInfo);
+ rewritingFlags
+ .getRetargetCoreLibMember()
+ .forEach(
+ (method, type) ->
+ convertRetargetCoreLibMemberFlag(builder, rewritingFlags, method, type));
+ }
+
+ private void convertRetargetCoreLibMemberFlag(
+ MachineRewritingFlags.Builder builder,
+ HumanRewritingFlags rewritingFlags,
+ DexMethod method,
+ DexType type) {
+ DexClass holder = appInfo.definitionFor(method.holder);
+ DexEncodedMethod foundMethod = holder.lookupMethod(method);
+ assert foundMethod != null;
+ if (foundMethod.isStatic()) {
+ convertStaticRetarget(builder, foundMethod, type);
+ return;
+ }
+ if (holder.isFinal() || foundMethod.isFinal()) {
+ convertNonEmulatedVirtualRetarget(builder, foundMethod, type);
+ return;
+ }
+ convertEmulatedVirtualRetarget(builder, rewritingFlags, foundMethod, type);
+ }
+
+ private void convertEmulatedVirtualRetarget(
+ MachineRewritingFlags.Builder builder,
+ HumanRewritingFlags rewritingFlags,
+ DexEncodedMethod src,
+ DexType type) {
+ if (isEmulatedInterfaceDispatch(src, appInfo, rewritingFlags)) {
+ // Handled by emulated interface dispatch.
+ return;
+ }
+ // TODO(b/184026720): Implement library boundaries.
+ DexProto newProto = appInfo.dexItemFactory().prependHolderToProto(src.getReference());
+ DexMethod forwardingDexMethod =
+ appInfo.dexItemFactory().createMethod(type, newProto, src.getName());
+ DerivedMethod forwardingMethod = new DerivedMethod(forwardingDexMethod);
+ DerivedMethod interfaceMethod =
+ new DerivedMethod(src.getReference(), SyntheticKind.RETARGET_INTERFACE);
+ DerivedMethod dispatchMethod =
+ new DerivedMethod(src.getReference(), SyntheticKind.RETARGET_CLASS);
+ LinkedHashMap<DexType, DerivedMethod> dispatchCases = new LinkedHashMap<>();
+ assert validateNoOverride(src, appInfo, subtypingInfo);
+ builder.putEmulatedVirtualRetarget(
+ src.getReference(),
+ new EmulatedDispatchMethodDescriptor(
+ interfaceMethod, dispatchMethod, forwardingMethod, dispatchCases));
+ }
+
+ private boolean validateNoOverride(
+ DexEncodedMethod src, AppInfoWithClassHierarchy appInfo, SubtypingInfo subtypingInfo) {
+ for (DexType subtype : subtypingInfo.subtypes(src.getHolderType())) {
+ DexClass subclass = appInfo.definitionFor(subtype);
+ MethodResolutionResult resolutionResult =
+ appInfo.resolveMethodOn(subclass, src.getReference());
+ if (resolutionResult.isSuccessfulMemberResolutionResult()
+ && resolutionResult.getResolvedMethod().getReference() != src.getReference()) {
+ assert false; // Unsupported.
+ }
+ }
+ return true;
+ }
+
+ private boolean isEmulatedInterfaceDispatch(
+ DexEncodedMethod method,
+ AppInfoWithClassHierarchy appInfo,
+ HumanRewritingFlags humanRewritingFlags) {
+ // Answers true if this method is already managed through emulated interface dispatch.
+ Map<DexType, DexType> emulateLibraryInterface =
+ humanRewritingFlags.getEmulateLibraryInterface();
+ if (emulateLibraryInterface.isEmpty()) {
+ return false;
+ }
+ DexMethod methodToFind = method.getReference();
+ // Look-up all superclass and interfaces, if an emulated interface is found,
+ // and it implements the method, answers true.
+ DexClass dexClass = appInfo.definitionFor(method.getHolderType());
+ // Cannot retarget a method on a virtual method on an emulated interface.
+ assert !emulateLibraryInterface.containsKey(dexClass.getType());
+ return appInfo
+ .traverseSuperTypes(
+ dexClass,
+ (supertype, subclass, isSupertypeAnInterface) ->
+ TraversalContinuation.breakIf(
+ subclass.isInterface()
+ && emulateLibraryInterface.containsKey(subclass.getType())
+ && subclass.lookupMethod(methodToFind) != null))
+ .shouldBreak();
+ }
+
+ 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) {
+ 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) {
+ convertNonEmulatedRetarget(
+ foundMethod, type, appInfo, subtypingInfo, builder::putStaticRetarget);
+ }
+}
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 d2227cb..69ae2e2 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
@@ -9,31 +9,17 @@
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.DexProto;
-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.DerivedMethod;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
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.synthesis.SyntheticNaming.SyntheticKind;
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 com.android.tools.r8.utils.TraversalContinuation;
import java.io.IOException;
import java.nio.file.Path;
-import java.util.LinkedHashMap;
-import java.util.Map;
import java.util.concurrent.ExecutorService;
-import java.util.function.BiConsumer;
public class HumanToMachineSpecificationConverter {
@@ -51,156 +37,12 @@
private MachineRewritingFlags convertRewritingFlags(
HumanRewritingFlags rewritingFlags, AppInfoWithClassHierarchy appInfo) {
MachineRewritingFlags.Builder builder = MachineRewritingFlags.builder();
- SubtypingInfo subtypingInfo = new SubtypingInfo(appInfo);
- rewritingFlags
- .getRetargetCoreLibMember()
- .forEach(
- (method, type) ->
- convertRetargetCoreLibMemberFlag(
- builder, rewritingFlags, method, type, appInfo, subtypingInfo));
+ new HumanToMachineRetargetConverter(appInfo).convertRetargetFlags(rewritingFlags, builder);
+ new HumanToMachineEmulatedInterfaceConverter(appInfo)
+ .convertEmulatedInterfaces(rewritingFlags, appInfo, builder);
return builder.build();
}
- private void convertRetargetCoreLibMemberFlag(
- MachineRewritingFlags.Builder builder,
- HumanRewritingFlags rewritingFlags,
- 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, rewritingFlags, foundMethod, type, appInfo, subtypingInfo);
- }
-
- private void convertEmulatedVirtualRetarget(
- MachineRewritingFlags.Builder builder,
- HumanRewritingFlags rewritingFlags,
- DexEncodedMethod src,
- DexType type,
- AppInfoWithClassHierarchy appInfo,
- SubtypingInfo subtypingInfo) {
- if (isEmulatedInterfaceDispatch(src, appInfo, rewritingFlags)) {
- // Handled by emulated interface dispatch.
- return;
- }
- // TODO(b/184026720): Implement library boundaries.
- DexProto newProto = appInfo.dexItemFactory().prependHolderToProto(src.getReference());
- DexMethod forwardingDexMethod =
- appInfo.dexItemFactory().createMethod(type, newProto, src.getName());
- DerivedMethod forwardingMethod = new DerivedMethod(forwardingDexMethod);
- DerivedMethod interfaceMethod =
- new DerivedMethod(src.getReference(), SyntheticKind.RETARGET_INTERFACE);
- DexMethod dispatchDexMethod =
- appInfo.dexItemFactory().createMethod(src.getHolderType(), newProto, src.getName());
- DerivedMethod dispatchMethod =
- new DerivedMethod(dispatchDexMethod, SyntheticKind.RETARGET_CLASS);
- LinkedHashMap<DexType, DerivedMethod> dispatchCases = new LinkedHashMap<>();
- assert validateNoOverride(src, appInfo, subtypingInfo);
- builder.putEmulatedVirtualRetarget(
- src.getReference(),
- new EmulatedDispatchMethodDescriptor(
- interfaceMethod, dispatchMethod, forwardingMethod, dispatchCases));
- }
-
- private boolean validateNoOverride(
- DexEncodedMethod src, AppInfoWithClassHierarchy appInfo, SubtypingInfo subtypingInfo) {
- for (DexType subtype : subtypingInfo.subtypes(src.getHolderType())) {
- DexClass subclass = appInfo.definitionFor(subtype);
- MethodResolutionResult resolutionResult =
- appInfo.resolveMethodOn(subclass, src.getReference());
- if (resolutionResult.isSuccessfulMemberResolutionResult()
- && resolutionResult.getResolvedMethod().getReference() != src.getReference()) {
- assert false; // Unsupported.
- }
- }
- return true;
- }
-
- private boolean isEmulatedInterfaceDispatch(
- DexEncodedMethod method,
- AppInfoWithClassHierarchy appInfo,
- HumanRewritingFlags humanRewritingFlags) {
- // Answers true if this method is already managed through emulated interface dispatch.
- Map<DexType, DexType> emulateLibraryInterface =
- humanRewritingFlags.getEmulateLibraryInterface();
- if (emulateLibraryInterface.isEmpty()) {
- return false;
- }
- DexMethod methodToFind = method.getReference();
- // Look-up all superclass and interfaces, if an emulated interface is found,
- // and it implements the method, answers true.
- DexClass dexClass = appInfo.definitionFor(method.getHolderType());
- // Cannot retarget a method on a virtual method on an emulated interface.
- assert !emulateLibraryInterface.containsKey(dexClass.getType());
- return appInfo
- .traverseSuperTypes(
- dexClass,
- (supertype, subclass, isSupertypeAnInterface) ->
- TraversalContinuation.breakIf(
- subclass.isInterface()
- && emulateLibraryInterface.containsKey(subclass.getType())
- && subclass.lookupMethod(methodToFind) != null))
- .shouldBreak();
- }
-
- 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 =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
index 55cf273..3e8b5cb 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
@@ -4,21 +4,25 @@
package com.android.tools.r8.ir.desugar.itf;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.CfCode;
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.DexProgramClass;
+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.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaring;
import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedInterfaceDescriptor;
import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceSynthesizerEventConsumer.L8ProgramEmulatedInterfaceSynthesizerEventConsumer;
import com.android.tools.r8.ir.synthetic.EmulateDispatchSyntheticCfCodeProvider;
import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
import com.android.tools.r8.synthesis.SyntheticNaming;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.Iterables;
@@ -116,28 +120,34 @@
method ->
builder.addMethod(
methodBuilder ->
- synthesizeEmulatedInterfaceMethod(method, emulatedInterface, methodBuilder)));
- assert builder.getType()
- == InterfaceDesugaringSyntheticHelper.getEmulateLibraryInterfaceClassType(
- emulatedInterface.type, appView.dexItemFactory());
+ synthesizeEmulatedInterfaceMethod(
+ method, emulatedInterface, builder.getType(), methodBuilder)));
+ }
+
+ private DexMethod emulatedMethod(DerivedMethod method, DexType holder) {
+ assert method.getHolderKind() == SyntheticKind.EMULATED_INTERFACE_CLASS;
+ DexProto newProto = appView.dexItemFactory().prependHolderToProto(method.getMethod());
+ return appView.dexItemFactory().createMethod(holder, newProto, method.getName());
+ }
+
+ private DexMethod interfaceMethod(DerivedMethod method) {
+ assert method.getHolderKind() == null;
+ return method.getMethod();
}
private void synthesizeEmulatedInterfaceMethod(
- ProgramMethod method, DexProgramClass theInterface, SyntheticMethodBuilder methodBuilder) {
+ ProgramMethod method,
+ DexProgramClass theInterface,
+ DexType dispatchType,
+ SyntheticMethodBuilder methodBuilder) {
assert !method.getDefinition().isStatic();
+ if (appView.options().testing.machineDesugaredLibrarySpecification != null) {
+ synthesizeEmulatedInterfaceMethodFromMachineSpecification(
+ method, theInterface, dispatchType, methodBuilder);
+ return;
+ }
DexMethod emulatedMethod = helper.emulateInterfaceLibraryMethod(method);
- methodBuilder
- .setName(emulatedMethod.getName())
- .setProto(emulatedMethod.getProto())
- .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
- .setCode(
- emulatedInterfaceMethod ->
- synthesizeCfCode(method.asProgramMethod(), theInterface, emulatedInterfaceMethod));
- }
-
- private CfCode synthesizeCfCode(
- ProgramMethod method, DexProgramClass theInterface, DexMethod emulatedInterfaceMethod) {
- DexMethod libraryMethod =
+ DexMethod itfMethod =
method
.getReference()
.withHolder(helper.getEmulatedInterface(theInterface.type), appView.dexItemFactory());
@@ -145,13 +155,84 @@
helper.ensureDefaultAsMethodOfProgramCompanionClassStub(method).getReference();
LinkedHashMap<DexType, DexMethod> extraDispatchCases =
getDispatchCases(method, theInterface, companionMethod);
- return new EmulateDispatchSyntheticCfCodeProvider(
- emulatedInterfaceMethod.getHolderType(),
- companionMethod,
- libraryMethod,
- extraDispatchCases,
- appView)
- .generateCfCode();
+ methodBuilder
+ .setName(emulatedMethod.getName())
+ .setProto(emulatedMethod.getProto())
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setCode(
+ emulatedInterfaceMethod ->
+ new EmulateDispatchSyntheticCfCodeProvider(
+ emulatedMethod.getHolderType(),
+ companionMethod,
+ itfMethod,
+ extraDispatchCases,
+ appView)
+ .generateCfCode());
+ }
+
+ private void synthesizeEmulatedInterfaceMethodFromMachineSpecification(
+ ProgramMethod method,
+ DexProgramClass theInterface,
+ DexType dispatchType,
+ SyntheticMethodBuilder methodBuilder) {
+ EmulatedInterfaceDescriptor emulatedInterfaceDescriptor =
+ appView
+ .options()
+ .testing
+ .machineDesugaredLibrarySpecification
+ .getRewritingFlags()
+ .getEmulatedInterfaces()
+ .get(theInterface.type);
+ EmulatedDispatchMethodDescriptor descriptor =
+ emulatedInterfaceDescriptor.getEmulatedMethods().get(method.getReference());
+ DexMethod emulatedMethod = emulatedMethod(descriptor.getEmulatedDispatchMethod(), dispatchType);
+ DexMethod itfMethod = interfaceMethod(descriptor.getInterfaceMethod());
+ // TODO(b/184026720): Adapt to use the forwarding method.
+ DerivedMethod forwardingMethod = descriptor.getForwardingMethod();
+ assert forwardingMethod.getHolderKind() == SyntheticKind.COMPANION_CLASS;
+ assert forwardingMethod.getMethod() == method.getReference();
+ DexMethod companionMethod =
+ helper.ensureDefaultAsMethodOfProgramCompanionClassStub(method).getReference();
+ LinkedHashMap<DexType, DexMethod> extraDispatchCases = resolveDispatchCases(descriptor);
+ methodBuilder
+ .setName(descriptor.getEmulatedDispatchMethod().getName())
+ .setProto(descriptor.getEmulatedDispatchMethod().getProto())
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setCode(
+ emulatedInterfaceMethod ->
+ new EmulateDispatchSyntheticCfCodeProvider(
+ emulatedMethod.getHolderType(),
+ companionMethod,
+ itfMethod,
+ extraDispatchCases,
+ appView)
+ .generateCfCode());
+ }
+
+ private LinkedHashMap<DexType, DexMethod> resolveDispatchCases(
+ EmulatedDispatchMethodDescriptor descriptor) {
+ LinkedHashMap<DexType, DexMethod> extraDispatchCases = new LinkedHashMap<>();
+ descriptor
+ .getDispatchCases()
+ .forEach(
+ (type, derivedMethod) -> {
+ DexMethod caseMethod;
+ if (derivedMethod.getHolderKind() == null) {
+ caseMethod = derivedMethod.getMethod();
+ } else {
+ assert derivedMethod.getHolderKind() == SyntheticKind.COMPANION_CLASS;
+ ProgramMethod resolvedProgramMethod =
+ appView
+ .appInfoForDesugaring()
+ .resolveMethod(derivedMethod.getMethod(), true)
+ .getResolvedProgramMethod();
+ caseMethod =
+ InterfaceDesugaringSyntheticHelper.defaultAsMethodOfCompanionClass(
+ resolvedProgramMethod.getReference(), appView.dexItemFactory());
+ }
+ extraDispatchCases.put(type, caseMethod);
+ });
+ return extraDispatchCases;
}
private LinkedHashMap<DexType, DexMethod> getDispatchCases(
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/EmulateDispatchSyntheticCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/EmulateDispatchSyntheticCfCodeProvider.java
index e356796..a82579e 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/EmulateDispatchSyntheticCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/EmulateDispatchSyntheticCfCodeProvider.java
@@ -32,7 +32,6 @@
public class EmulateDispatchSyntheticCfCodeProvider extends SyntheticCfCodeProvider {
private final DexMethod forwardingMethod;
- private final DexType receiverType;
private final DexMethod interfaceMethod;
private final LinkedHashMap<DexType, DexMethod> extraDispatchCases;
@@ -44,13 +43,13 @@
AppView<?> appView) {
super(appView, holder);
this.forwardingMethod = forwardingMethod;
- this.receiverType = forwardingMethod.getParameter(0);
this.interfaceMethod = interfaceMethod;
this.extraDispatchCases = extraDispatchCases;
}
@Override
public CfCode generateCfCode() {
+ DexType receiverType = forwardingMethod.getParameter(0);
List<CfInstruction> instructions = new ArrayList<>();
CfLabel[] labels = new CfLabel[extraDispatchCases.size() + 1];
for (int i = 0; i < labels.length; i++) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
index b3eb86b..d1e9746 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
@@ -10,11 +10,17 @@
import com.android.tools.r8.D8TestRunResult;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.HumanToMachineSpecificationConverter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.LegacyToHumanSpecificationConverter;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
@@ -34,14 +40,19 @@
private final TestParameters parameters;
private final boolean shrinkDesugaredLibrary;
+ private final boolean machineSpec;
- @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+ @Parameters(name = "machine: {0}, {2}, shrink: {1}")
public static List<Object[]> data() {
return buildParameters(
- BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ BooleanUtils.values(),
+ BooleanUtils.values(),
+ getTestParameters().withDexRuntimes().withAllApiLevels().build());
}
- public CustomCollectionTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ public CustomCollectionTest(
+ boolean machineSpec, boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ this.machineSpec = machineSpec;
this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
this.parameters = parameters;
}
@@ -49,6 +60,22 @@
private final String EXECUTOR =
"com.android.tools.r8.desugar.desugaredlibrary.CustomCollectionTest$Executor";
+ private void setMachineSpec(InternalOptions opt) {
+ if (!machineSpec) {
+ return;
+ }
+ try {
+ HumanDesugaredLibrarySpecification human =
+ new LegacyToHumanSpecificationConverter()
+ .convert(opt.desugaredLibrarySpecification, getLibraryFile(), opt);
+ MachineDesugaredLibrarySpecification machine =
+ new HumanToMachineSpecificationConverter().convert(human, getLibraryFile(), opt);
+ opt.testing.machineDesugaredLibrarySpecification = machine;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
@Test
public void testCustomCollectionD8() throws Exception {
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
@@ -56,6 +83,7 @@
testForD8()
.addLibraryFiles(getLibraryFile())
.addInnerClasses(CustomCollectionTest.class)
+ .addOptionsModification(this::setMachineSpec)
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
@@ -78,6 +106,7 @@
Path jar =
testForD8(Backend.CF)
.addInnerClasses(CustomCollectionTest.class)
+ .addOptionsModification(this::setMachineSpec)
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
@@ -133,6 +162,7 @@
testForR8(Backend.DEX)
.addLibraryFiles(getLibraryFile())
.addInnerClasses(CustomCollectionTest.class)
+ .addOptionsModification(this::setMachineSpec)
.setMinApi(parameters.getApiLevel())
.addKeepClassAndMembersRules(Executor.class)
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)