Revert "Compute live methods for instantiated types based on super hierarchy."
This reverts commit 8778308da343628c311ceba61f3eb639b6925a09.
Bug: 149823703
Bug: 239464956
Reason for revert: b/149823703
Change-Id: I291201af0039872b5e5e11ed1a68fea8f6a2902f
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
index 9a07dd2..813ea84 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.WorkList;
+import com.android.tools.r8.utils.WorkList.EqualityTest;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -41,7 +42,7 @@
DexType type,
Consumer<DexProgramClass> subTypeConsumer,
Consumer<DexCallSite> callSiteConsumer) {
- WorkList<DexType> workList = WorkList.newIdentityWorkList();
+ WorkList<DexType> workList = new WorkList<>(EqualityTest.IDENTITY);
workList.addIfNotSeen(type);
workList.addIfNotSeen(allImmediateSubtypes(type));
while (workList.hasNext()) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index c474718..0ca6685 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -821,15 +821,6 @@
return !clinit.getOptimizationInfo().classInitializerMayBePostponed();
}
- public void forEachImmediateSupertype(Consumer<DexType> fn) {
- if (superType != null) {
- fn.accept(superType);
- }
- for (DexType iface : interfaces.values) {
- fn.accept(iface);
- }
- }
-
public Iterable<DexType> allImmediateSupertypes() {
Iterator<DexType> iterator =
superType != null
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index a960171..d431886 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -31,7 +31,6 @@
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexClasspathClass;
import com.android.tools.r8.graph.DexDefinition;
import com.android.tools.r8.graph.DexEncodedField;
@@ -55,7 +54,6 @@
import com.android.tools.r8.graph.FieldAccessInfoImpl;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.LookupResult;
-import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl;
import com.android.tools.r8.graph.PresortedComparable;
import com.android.tools.r8.graph.ProgramMethod;
@@ -97,7 +95,6 @@
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.Timing;
-import com.android.tools.r8.utils.WorkList;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
@@ -293,11 +290,8 @@
private final Set<DexEncodedMethod> pendingReflectiveUses = Sets.newLinkedHashSet();
/** A cache for DexMethod that have been marked reachable. */
- private final Map<DexProgramClass, Set<DexEncodedMethod>> reachableVirtualResolutions =
- new IdentityHashMap<>();
-
private final Map<DexMethod, MarkedResolutionTarget> virtualTargetsMarkedAsReachable =
- new IdentityHashMap<>();
+ Maps.newIdentityHashMap();
/**
* A set of references we have reported missing to dedupe warnings.
@@ -421,24 +415,22 @@
return getProgramClassOrNull(type) != null;
}
- private DexClass definitionFor(DexType type) {
- DexClass clazz = appView.definitionFor(type);
- if (clazz == null) {
- reportMissingClass(type);
- return null;
- }
- if (liveNonProgramTypes.add(clazz) && clazz.isLibraryClass()) {
- // TODO(b/149201735): This likely needs to apply to classpath too.
- ensureMethodsContinueToWidenAccess(clazz);
- // Only libraries must not derive program. Classpath classes can, assuming correct keep rules.
- warnIfLibraryTypeInheritsFromProgramType(clazz.asLibraryClass());
- }
- return clazz;
- }
-
private DexProgramClass getProgramClassOrNull(DexType type) {
- DexClass clazz = definitionFor(type);
- return clazz != null && clazz.isProgramClass() ? clazz.asProgramClass() : null;
+ DexClass clazz = appView.definitionFor(type);
+ if (clazz != null) {
+ if (clazz.isProgramClass()) {
+ return clazz.asProgramClass();
+ }
+ if (liveNonProgramTypes.add(clazz) && clazz.isLibraryClass()) {
+ // TODO(b/149201735): This likely needs to apply to classpath too.
+ ensureMethodsContinueToWidenAccess(clazz);
+ // TODO(b/149201158): This should apply to classpath too (likely even hard fail).
+ warnIfLibraryTypeInheritsFromProgramType(clazz.asLibraryClass());
+ }
+ } else {
+ reportMissingClass(type);
+ }
+ return null;
}
private void warnIfLibraryTypeInheritsFromProgramType(DexLibraryClass clazz) {
@@ -733,7 +725,6 @@
// we have to look at the interface chain and mark default methods as reachable, not taking
// the shadowing of other interface chains into account.
// See https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.3
- // TODO(b/148271337): Support lookupVirtualDispatchTarget(CallSite) and replace this.
ScopedDexMethodSet seen = new ScopedDexMethodSet();
for (DexType iface : descriptor.interfaces) {
DexProgramClass ifaceClazz = getProgramClassOrNull(iface);
@@ -1696,107 +1687,95 @@
}
/**
- * Marks all methods live that are overrides of reachable methods for a given class.
+ * Marks all methods live that can be reached by calls previously seen.
*
- * <p>Only reachable methods in the hierarchy of the given class and above are considered, and
- * only the lowest such reachable target (ie, mirroring resolution). All library and classpath
- * methods are considered reachable.
+ * <p>This should only be invoked if the given type newly becomes instantiated. In essence, this
+ * method replays all the invokes we have seen so far that could apply to this type and marks the
+ * corresponding methods live.
+ *
+ * <p>Only methods that are visible in this type are considered. That is, only those methods that
+ * are either defined directly on this type or that are defined on a supertype but are not
+ * shadowed by another inherited method. Furthermore, default methods from implemented interfaces
+ * that are not otherwise shadowed are considered, too.
+ *
+ * <p>Finally all methods on library types that resolve starting at the instantiated type are
+ * marked live.
*/
private void transitionMethodsForInstantiatedClass(DexProgramClass instantiatedClass) {
- assert !instantiatedClass.isAnnotation();
- assert !instantiatedClass.isInterface();
ScopedDexMethodSet seen = new ScopedDexMethodSet();
- WorkList<DexType> worklist = WorkList.newIdentityWorkList();
- // First we lookup and mark all targets on the instantiated class for each reachable method in
- // the super chain (inclusive).
- {
- DexClass clazz = instantiatedClass;
- while (clazz != null) {
- if (clazz.isProgramClass()) {
- markProgramMethodOverridesAsLive(instantiatedClass, clazz.asProgramClass(), seen);
- } else {
- markLibraryAndClasspathMethodOverridesAsLive(instantiatedClass, clazz);
+ Set<DexType> interfaces = Sets.newIdentityHashSet();
+ DexProgramClass current = instantiatedClass;
+ do {
+ // We only have to look at virtual methods here, as only those can actually be executed at
+ // runtime. Illegal dispatch situations and the corresponding exceptions are already handled
+ // by the reachability logic.
+ transitionReachableVirtualMethods(current, seen);
+ Collections.addAll(interfaces, current.interfaces.values);
+ current = getProgramClassOrNull(current.superType);
+ } while (current != null && !objectAllocationInfoCollection.isInstantiatedDirectly(current));
+
+ // The set now contains all virtual methods on the type and its supertype that are reachable.
+ // In a second step, we now look at interfaces. We have to do this in this order due to JVM
+ // semantics for default methods. A default method is only reachable if it is not overridden in
+ // any superclass. Also, it is not defined which default method is chosen if multiple
+ // interfaces define the same default method. Hence, for every interface (direct or indirect),
+ // we have to look at the interface chain and mark default methods as reachable, not taking
+ // the shadowing of other interface chains into account.
+ // See https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.3
+ for (DexType iface : interfaces) {
+ DexClass clazz = appView.definitionFor(iface);
+ if (clazz == null) {
+ reportMissingClass(iface);
+ // TODO(herhut): In essence, our subtyping chain is broken here. Handle that case better.
+ break;
+ }
+ transitionDefaultMethodsForInstantiatedClass(iface, seen);
+ }
+
+ // When tracing the main-dex content, library roots must be specified, thus there are no
+ // implicit edges from library methods.
+ if (getMode().isTracingMainDex()) {
+ return;
+ }
+
+ // When a type becomes live, all library methods on that type become live too.
+ // This is done by searching the library supertypes and then resolving each method defined by
+ // such a library type from the point of the instantiated type. If the resolved targets are in
+ // the program, i.e., the instantiated type has a method overidding a library method, then the
+ // program method is live.
+ Deque<DexClass> librarySearchItems = new ArrayDeque<>();
+ librarySearchItems.add(instantiatedClass);
+ while (!librarySearchItems.isEmpty()) {
+ DexClass clazz = librarySearchItems.pop();
+ if (clazz.isNotProgramClass()) {
+ markLibraryAndClasspathMethodOverridesAsLive(clazz, instantiatedClass);
+ }
+ if (clazz.superType != null) {
+ DexClass superClass = appView.definitionFor(clazz.superType);
+ if (superClass != null) {
+ librarySearchItems.add(superClass);
}
- worklist.addIfNotSeen(Arrays.asList(clazz.interfaces.values));
- clazz = clazz.superType != null ? definitionFor(clazz.superType) : null;
+ }
+ for (DexType iface : clazz.interfaces.values) {
+ DexClass ifaceClass = appView.definitionFor(iface);
+ if (ifaceClass != null) {
+ librarySearchItems.add(ifaceClass);
+ }
}
}
- // The targets for methods on the type and its supertype that are reachable are now marked.
- // In a second step, we look at interfaces. We order the search this way such that a
- // method reachable on a class takes precedence when reporting edges. That order mirrors JVM
- // resolution/dispatch.
- while (worklist.hasNext()) {
- DexType type = worklist.next();
- DexClass iface = definitionFor(type);
- if (iface == null) {
- continue;
- }
- assert iface.superType == appInfo.dexItemFactory().objectType;
- if (iface.isNotProgramClass()) {
- markLibraryAndClasspathMethodOverridesAsLive(instantiatedClass, iface);
- } else {
- markProgramMethodOverridesAsLive(instantiatedClass, iface.asProgramClass(), seen);
- }
- worklist.addIfNotSeen(Arrays.asList(iface.interfaces.values));
- }
- }
-
- private Set<DexEncodedMethod> getReachableVirtualResolutions(DexProgramClass clazz) {
- return reachableVirtualResolutions.getOrDefault(clazz, Collections.emptySet());
- }
-
- private void markProgramMethodOverridesAsLive(
- DexProgramClass instantiatedClass,
- DexProgramClass superClass,
- ScopedDexMethodSet seenMethods) {
- for (DexEncodedMethod resolution : getReachableVirtualResolutions(superClass)) {
- if (seenMethods.addMethod(resolution)) {
- markLiveOverrides(instantiatedClass, superClass, resolution);
- }
- }
- }
-
- private void markLiveOverrides(
- DexProgramClass instantiatedClass,
- DexProgramClass reachableHolder,
- DexEncodedMethod reachableMethod) {
- assert reachableHolder.type == reachableMethod.method.holder;
- // The validity of the reachable method is checked at the point it becomes "reachable" and is
- // resolved. If the method is private, then the dispatch is not "virtual" and the method is
- // simply marked live on its holder.
- if (reachableMethod.isPrivateMethod()) {
- markVirtualMethodAsLive(
- reachableHolder,
- reachableMethod,
- graphReporter.reportReachableMethodAsLive(
- reachableMethod.method, new ProgramMethod(reachableHolder, reachableMethod)));
- return;
- }
- // Otherwise, we set the initial holder type to be the holder of the reachable method, which
- // ensures that access will be generally valid.
- SingleResolutionResult result =
- new SingleResolutionResult(reachableHolder, reachableHolder, reachableMethod);
- DexClassAndMethod lookup = result.lookupVirtualDispatchTarget(instantiatedClass, appView);
- if (lookup == null || !lookup.isProgramMethod() || lookup.getMethod().isAbstract()) {
- return;
- }
- ProgramMethod method = lookup.asProgramMethod();
- markVirtualMethodAsLive(
- method.getHolder(),
- method.getMethod(),
- graphReporter.reportReachableMethodAsLive(reachableMethod.method, method));
}
private void markLibraryAndClasspathMethodOverridesAsLive(
- DexProgramClass instantiatedClass, DexClass libraryClass) {
+ DexClass libraryClass, DexProgramClass instantiatedClass) {
assert libraryClass.isNotProgramClass();
assert !instantiatedClass.isInterface() || instantiatedClass.isAnnotation();
for (DexEncodedMethod method : libraryClass.virtualMethods()) {
- assert !method.isPrivateMethod();
- // Note: It would be reasonable to not process methods already seen during the marking of
- // program usages, but that would cause the methods to not be marked as library overrides.
- markLibraryOrClasspathOverrideLive(
- instantiatedClass, libraryClass, appInfo.resolveMethod(libraryClass, method.method));
+ // Note: it may be worthwhile to add a resolution cache here. If so, it must still ensure
+ // that all library override edges are reported to the kept-graph consumer.
+ ResolutionResult firstResolution =
+ appView.appInfo().resolveMethod(instantiatedClass, method.method);
+ markResolutionAsLive(libraryClass, firstResolution);
+ markOverridesAsLibraryMethodOverrides(method.method, instantiatedClass);
// Due to API conversion, some overrides can be hidden since they will be rewritten. See
// class comment of DesugaredLibraryAPIConverter and vivifiedType logic.
@@ -1808,50 +1787,47 @@
DesugaredLibraryAPIConverter.methodWithVivifiedTypeInSignature(
method.method, method.method.holder, appView);
assert methodToResolve != method.method;
- markLibraryOrClasspathOverrideLive(
- instantiatedClass,
- libraryClass,
- appInfo.resolveMethod(instantiatedClass, methodToResolve));
+ ResolutionResult secondResolution =
+ appView.appInfo().resolveMethod(instantiatedClass, methodToResolve);
+ markResolutionAsLive(libraryClass, secondResolution);
+ markOverridesAsLibraryMethodOverrides(methodToResolve, instantiatedClass);
}
+
}
}
- private void markLibraryOrClasspathOverrideLive(
- DexProgramClass instantiatedClass,
- DexClass libraryOrClasspathClass,
- ResolutionResult resolution) {
- DexClassAndMethod lookup = resolution.lookupVirtualDispatchTarget(instantiatedClass, appView);
- if (lookup == null || !lookup.isProgramMethod() || lookup.getMethod().isAbstract()) {
- return;
+ private void markResolutionAsLive(DexClass libraryClass, ResolutionResult resolution) {
+ if (resolution.isVirtualTarget()) {
+ DexEncodedMethod target = resolution.getSingleTarget();
+ DexProgramClass targetHolder = getProgramClassOrNull(target.method.holder);
+ if (targetHolder != null
+ && shouldMarkLibraryMethodOverrideAsReachable(targetHolder, target)) {
+ markVirtualMethodAsLive(
+ targetHolder, target, KeepReason.isLibraryMethod(targetHolder, libraryClass.type));
+ }
}
- DexProgramClass clazz = lookup.asProgramMethod().getHolder();
- DexEncodedMethod target = lookup.getMethod();
- if (shouldMarkLibraryMethodOverrideAsReachable(clazz, target)) {
- markVirtualMethodAsLive(
- clazz, target, KeepReason.isLibraryMethod(clazz, libraryOrClasspathClass.type));
- }
- markOverridesAsLibraryMethodOverrides(target, instantiatedClass);
}
private void markOverridesAsLibraryMethodOverrides(
- DexEncodedMethod libraryMethodOverride, DexProgramClass instantiatedClass) {
- libraryMethodOverride.setLibraryMethodOverride(OptionalBool.TRUE);
- WorkList<DexType> worklist = WorkList.newIdentityWorkList();
- instantiatedClass.forEachImmediateSupertype(worklist::addIfNotSeen);
- while (worklist.hasNext()) {
- DexType type = worklist.next();
- DexProgramClass clazz = getProgramClassOrNull(type);
- if (clazz == null) {
- continue;
- }
- DexEncodedMethod override = clazz.lookupVirtualMethod(libraryMethodOverride.method);
- if (override != null) {
- if (override.isLibraryMethodOverride().isTrue()) {
+ DexMethod libraryMethod, DexProgramClass instantiatedClass) {
+ Set<DexProgramClass> visited = SetUtils.newIdentityHashSet(instantiatedClass);
+ Deque<DexProgramClass> worklist = DequeUtils.newArrayDeque(instantiatedClass);
+ while (!worklist.isEmpty()) {
+ DexProgramClass clazz = worklist.removeFirst();
+ assert visited.contains(clazz);
+ DexEncodedMethod libraryMethodOverride = clazz.lookupVirtualMethod(libraryMethod);
+ if (libraryMethodOverride != null) {
+ if (libraryMethodOverride.isLibraryMethodOverride().isTrue()) {
continue;
}
- override.setLibraryMethodOverride(OptionalBool.TRUE);
+ libraryMethodOverride.setLibraryMethodOverride(OptionalBool.TRUE);
}
- clazz.forEachImmediateSupertype(worklist::addIfNotSeen);
+ for (DexType superType : clazz.allImmediateSupertypes()) {
+ DexProgramClass superClass = getProgramClassOrNull(superType);
+ if (superClass != null && visited.add(superClass)) {
+ worklist.add(superClass);
+ }
+ }
}
}
@@ -2197,10 +2173,6 @@
// each possible target edge below.
assert resolution.holder.isProgramClass();
- reachableVirtualResolutions
- .computeIfAbsent(resolution.holder.asProgramClass(), k -> Sets.newIdentityHashSet())
- .add(resolution.method);
-
assert interfaceInvoke == holder.isInterface();
DexProgramClass context = contextOrNull == null ? null : contextOrNull.getHolder();
LookupResult lookupResult =
@@ -2210,12 +2182,11 @@
if (!lookupResult.isLookupResultSuccess()) {
return;
}
- LookupResultSuccess lookupResultSuccess = lookupResult.asLookupResultSuccess();
- for (DexEncodedMethod encodedPossibleTarget : lookupResultSuccess.getMethodTargets()) {
+ for (DexEncodedMethod encodedPossibleTarget :
+ lookupResult.asLookupResultSuccess().getMethodTargets()) {
if (encodedPossibleTarget.isAbstract()) {
continue;
}
- // TODO(b/139464956): Replace this downwards search once targets are found for live types.
markPossibleTargetsAsReachable(resolution, encodedPossibleTarget);
}
}
@@ -2227,8 +2198,20 @@
assert !encodedPossibleTarget.isAbstract();
DexMethod possibleTarget = encodedPossibleTarget.method;
DexProgramClass clazz = getProgramClassOrNull(possibleTarget.holder);
- // If the holder type is uninstantiated (directly or indirectly) the method is not live yet.
- if (clazz == null || !isInstantiatedOrHasInstantiatedSubtype(clazz)) {
+ if (clazz == null) {
+ return;
+ }
+ ReachableVirtualMethodsSet reachable =
+ reachableVirtualMethods.computeIfAbsent(clazz, ignore -> new ReachableVirtualMethodsSet());
+ if (!reachable.add(encodedPossibleTarget, reason)) {
+ return;
+ }
+
+ // If the holder type is instantiated, the method is live. Otherwise check whether we find
+ // a subtype that does not shadow this methods but is instantiated.
+ // Note that library classes are always considered instantiated, as we do not know where
+ // they are instantiated.
+ if (!isInstantiatedOrHasInstantiatedSubtype(clazz)) {
return;
}
@@ -2237,8 +2220,7 @@
markVirtualMethodAsLive(
clazz,
encodedPossibleTarget,
- graphReporter.reportReachableMethodAsLive(
- reason.method.method, new ProgramMethod(clazz, encodedPossibleTarget)));
+ graphReporter.reportReachableMethodAsLive(encodedPossibleTarget, reason));
} else {
Deque<DexType> worklist =
new ArrayDeque<>(appInfo.allImmediateSubtypes(possibleTarget.holder));
diff --git a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
index 37be4b3..c172b52 100644
--- a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
+++ b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
@@ -26,7 +26,6 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
@@ -249,17 +248,6 @@
}
public KeepReasonWitness reportReachableMethodAsLive(
- DexMethod overriddenMethod, ProgramMethod derivedMethod) {
- if (keptGraphConsumer != null && overriddenMethod != derivedMethod.getMethod().method) {
- return reportEdge(
- getMethodGraphNode(overriddenMethod),
- getMethodGraphNode(derivedMethod.getMethod().method),
- EdgeKind.OverridingMethod);
- }
- return KeepReasonWitness.INSTANCE;
- }
-
- public KeepReasonWitness reportReachableMethodAsLive(
DexEncodedMethod encodedMethod, MarkedResolutionTarget reason) {
if (keptGraphConsumer != null) {
return reportEdge(
diff --git a/src/main/java/com/android/tools/r8/utils/WorkList.java b/src/main/java/com/android/tools/r8/utils/WorkList.java
index 835c528..b631262 100644
--- a/src/main/java/com/android/tools/r8/utils/WorkList.java
+++ b/src/main/java/com/android/tools/r8/utils/WorkList.java
@@ -15,15 +15,7 @@
private final Deque<T> workingList = new ArrayDeque<>();
private final Set<T> seen;
- public static <T> WorkList<T> newIdentityWorkList() {
- return new WorkList<T>(EqualityTest.IDENTITY);
- }
-
- public WorkList() {
- this(EqualityTest.HASH);
- }
-
- private WorkList(EqualityTest equalityTest) {
+ public WorkList(EqualityTest equalityTest) {
if (equalityTest == EqualityTest.HASH) {
seen = new HashSet<>();
} else {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java
index 687a673..66cdcbc 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FunctionConversionTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.conversiontests.MoreFunctionConversionTest.CustomLibClass;
import com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;