Merge commit '17024bf79ef3ea7a81018e3bd7b67358e328c3d3' into dev-release
Change-Id: Iee886bcc9c9cfe23e1a57969fdb239e19cb5ea31
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs.json b/src/library_desugar/jdk11/desugar_jdk_libs.json
index 3d23d9f..0020bab 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs.json
@@ -403,12 +403,34 @@
"java.lang.Object java.util.concurrent.atomic.AtomicReference#getAndAccumulate(java.lang.Object, java.util.function.BinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicReference",
"java.lang.Object java.util.concurrent.atomic.AtomicReference#getAndUpdate(java.util.function.UnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicReference",
"java.lang.Object java.util.concurrent.atomic.AtomicReference#updateAndGet(java.util.function.UnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicReference",
+ "java.util.Collection java.util.Collections#checkedCollection(java.util.Collection, java.lang.Class)": "java.util.DesugarCollections",
+ "java.util.Set java.util.Collections#checkedSet(java.util.Set, java.lang.Class)": "java.util.DesugarCollections",
+ "java.util.SortedSet java.util.Collections#checkedSortedSet(java.util.SortedSet, java.lang.Class)": "java.util.DesugarCollections",
+ "java.util.List java.util.Collections#checkedList(java.util.List, java.lang.Class)": "java.util.DesugarCollections",
+ "java.util.Map java.util.Collections#checkedMap(java.util.Map, java.lang.Class, java.lang.Class)": "java.util.DesugarCollections",
+ "java.util.SortedMap java.util.Collections#checkedSortedMap(java.util.SortedMap, java.lang.Class, java.lang.Class)": "java.util.DesugarCollections",
+ "java.util.Collection java.util.Collections#unmodifiableCollection(java.util.Collection)": "java.util.DesugarCollections",
+ "java.util.Set java.util.Collections#unmodifiableSet(java.util.Set)": "java.util.DesugarCollections",
+ "java.util.SortedSet java.util.Collections#unmodifiableSortedSet(java.util.SortedSet)": "java.util.DesugarCollections",
+ "java.util.List java.util.Collections#unmodifiableList(java.util.List)": "java.util.DesugarCollections",
+ "java.util.Map java.util.Collections#unmodifiableMap(java.util.Map)": "java.util.DesugarCollections",
+ "java.util.SortedMap java.util.Collections#unmodifiableSortedMap(java.util.SortedMap)": "java.util.DesugarCollections",
+ "java.util.Collection java.util.Collections#synchronizedCollection(java.util.Collection)": "java.util.DesugarCollections",
+ "java.util.Set java.util.Collections#synchronizedSet(java.util.Set)": "java.util.DesugarCollections",
+ "java.util.SortedSet java.util.Collections#synchronizedSortedSet(java.util.SortedSet)": "java.util.DesugarCollections",
+ "java.util.List java.util.Collections#synchronizedList(java.util.List)": "java.util.DesugarCollections",
"java.util.Map java.util.Collections#synchronizedMap(java.util.Map)": "java.util.DesugarCollections",
"java.util.SortedMap java.util.Collections#synchronizedSortedMap(java.util.SortedMap)": "java.util.DesugarCollections",
"long java.util.concurrent.atomic.AtomicLong#accumulateAndGet(long, java.util.function.LongBinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong",
"long java.util.concurrent.atomic.AtomicLong#getAndAccumulate(long, java.util.function.LongBinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong",
"long java.util.concurrent.atomic.AtomicLong#getAndUpdate(java.util.function.LongUnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong",
"long java.util.concurrent.atomic.AtomicLong#updateAndGet(java.util.function.LongUnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong"
+ },
+ "api_generic_types_conversion": {
+ "java.util.Collection java.util.Hashtable#values()": [-1, "java.util.Collection java.util.DesugarCollections#bridge_synchronizedCollection(java.util.Collection, java.lang.Object)"],
+ "java.util.Set java.util.Hashtable#entrySet()": [-1, "java.util.Set java.util.DesugarCollections#bridge_synchronizedSet(java.util.Set, java.lang.Object)"],
+ "java.util.Set java.util.Hashtable#keySet()": [-1, "java.util.Set java.util.DesugarCollections#bridge_synchronizedSet(java.util.Set, java.lang.Object)"],
+ "java.util.List java.util.Vector#subList(int, int)": [-1, "java.util.List java.util.DesugarCollections#bridge_synchronizedList(java.util.List, java.lang.Object)"]
}
}
],
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
index 0f81af4..aa8b1c0 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
@@ -571,12 +571,34 @@
"java.lang.Object java.util.concurrent.atomic.AtomicReference#getAndAccumulate(java.lang.Object, java.util.function.BinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicReference",
"java.lang.Object java.util.concurrent.atomic.AtomicReference#getAndUpdate(java.util.function.UnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicReference",
"java.lang.Object java.util.concurrent.atomic.AtomicReference#updateAndGet(java.util.function.UnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicReference",
+ "java.util.Collection java.util.Collections#checkedCollection(java.util.Collection, java.lang.Class)": "java.util.DesugarCollections",
+ "java.util.Set java.util.Collections#checkedSet(java.util.Set, java.lang.Class)": "java.util.DesugarCollections",
+ "java.util.SortedSet java.util.Collections#checkedSortedSet(java.util.SortedSet, java.lang.Class)": "java.util.DesugarCollections",
+ "java.util.List java.util.Collections#checkedList(java.util.List, java.lang.Class)": "java.util.DesugarCollections",
+ "java.util.Map java.util.Collections#checkedMap(java.util.Map, java.lang.Class, java.lang.Class)": "java.util.DesugarCollections",
+ "java.util.SortedMap java.util.Collections#checkedSortedMap(java.util.SortedMap, java.lang.Class, java.lang.Class)": "java.util.DesugarCollections",
+ "java.util.Collection java.util.Collections#unmodifiableCollection(java.util.Collection)": "java.util.DesugarCollections",
+ "java.util.Set java.util.Collections#unmodifiableSet(java.util.Set)": "java.util.DesugarCollections",
+ "java.util.SortedSet java.util.Collections#unmodifiableSortedSet(java.util.SortedSet)": "java.util.DesugarCollections",
+ "java.util.List java.util.Collections#unmodifiableList(java.util.List)": "java.util.DesugarCollections",
+ "java.util.Map java.util.Collections#unmodifiableMap(java.util.Map)": "java.util.DesugarCollections",
+ "java.util.SortedMap java.util.Collections#unmodifiableSortedMap(java.util.SortedMap)": "java.util.DesugarCollections",
+ "java.util.Collection java.util.Collections#synchronizedCollection(java.util.Collection)": "java.util.DesugarCollections",
+ "java.util.Set java.util.Collections#synchronizedSet(java.util.Set)": "java.util.DesugarCollections",
+ "java.util.SortedSet java.util.Collections#synchronizedSortedSet(java.util.SortedSet)": "java.util.DesugarCollections",
+ "java.util.List java.util.Collections#synchronizedList(java.util.List)": "java.util.DesugarCollections",
"java.util.Map java.util.Collections#synchronizedMap(java.util.Map)": "java.util.DesugarCollections",
"java.util.SortedMap java.util.Collections#synchronizedSortedMap(java.util.SortedMap)": "java.util.DesugarCollections",
"long java.util.concurrent.atomic.AtomicLong#accumulateAndGet(long, java.util.function.LongBinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong",
"long java.util.concurrent.atomic.AtomicLong#getAndAccumulate(long, java.util.function.LongBinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong",
"long java.util.concurrent.atomic.AtomicLong#getAndUpdate(java.util.function.LongUnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong",
"long java.util.concurrent.atomic.AtomicLong#updateAndGet(java.util.function.LongUnaryOperator)": "java.util.concurrent.atomic.DesugarAtomicLong"
+ },
+ "api_generic_types_conversion": {
+ "java.util.Collection java.util.Hashtable#values()": [-1, "java.util.Collection java.util.DesugarCollections#bridge_synchronizedCollection(java.util.Collection, java.lang.Object)"],
+ "java.util.Set java.util.Hashtable#entrySet()": [-1, "java.util.Set java.util.DesugarCollections#bridge_synchronizedSet(java.util.Set, java.lang.Object)"],
+ "java.util.Set java.util.Hashtable#keySet()": [-1, "java.util.Set java.util.DesugarCollections#bridge_synchronizedSet(java.util.Set, java.lang.Object)"],
+ "java.util.List java.util.Vector#subList(int, int)": [-1, "java.util.List java.util.DesugarCollections#bridge_synchronizedList(java.util.List, java.lang.Object)"]
}
},
{
diff --git a/src/main/java/com/android/tools/r8/classmerging/Policy.java b/src/main/java/com/android/tools/r8/classmerging/Policy.java
index e7f52f3..7655762 100644
--- a/src/main/java/com/android/tools/r8/classmerging/Policy.java
+++ b/src/main/java/com/android/tools/r8/classmerging/Policy.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicyWithPreprocessing;
import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
-import com.android.tools.r8.verticalclassmerging.policies.VerticalClassMergerPolicy;
import com.android.tools.r8.verticalclassmerging.policies.VerticalClassMergerPolicyWithPreprocessing;
import java.util.ArrayList;
import java.util.Collection;
@@ -60,16 +59,7 @@
return false;
}
- public VerticalClassMergerPolicy asVerticalClassMergerPolicy() {
- return null;
- }
-
- public boolean isVerticalClassMergerPolicyWithPreprocessing() {
- return false;
- }
-
- public VerticalClassMergerPolicyWithPreprocessing<?>
- asVerticalClassMergerPolicyWithPreprocessing() {
+ public VerticalClassMergerPolicyWithPreprocessing<?> asVerticalClassMergerPolicy() {
return null;
}
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 ea46ea8..3c8c2fd 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -1141,9 +1141,8 @@
return null;
}
- @SuppressWarnings("ReferenceEquality")
public boolean isInSameNest(DexClass other) {
- return isInANest() && other.isInANest() && getNestHost() == other.getNestHost();
+ return isInANest() && other.isInANest() && getNestHost().isIdenticalTo(other.getNestHost());
}
public void forEachNestMember(Consumer<DexType> consumer) {
diff --git a/src/main/java/com/android/tools/r8/utils/ArchiveResourceProvider.java b/src/main/java/com/android/tools/r8/utils/ArchiveResourceProvider.java
index 6532b8f..b73119d 100644
--- a/src/main/java/com/android/tools/r8/utils/ArchiveResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/utils/ArchiveResourceProvider.java
@@ -93,8 +93,10 @@
}
if (!dexResources.isEmpty() && !classResources.isEmpty()) {
throw new CompilationError(
- "Cannot create android app from an archive '" + archive
- + "' containing both DEX and Java-bytecode content");
+ "Cannot create android app from an archive '"
+ + archive
+ + "' containing both DEX and Java-bytecode content",
+ origin);
}
return !dexResources.isEmpty() ? dexResources : classResources;
}
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/verticalclassmerging/ClassMerger.java
index a198871..86b9848 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/ClassMerger.java
@@ -8,6 +8,7 @@
import static com.android.tools.r8.ir.code.InvokeType.DIRECT;
import static com.android.tools.r8.ir.code.InvokeType.STATIC;
import static com.android.tools.r8.ir.code.InvokeType.VIRTUAL;
+import static java.util.function.Predicate.not;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.errors.Unreachable;
@@ -93,6 +94,77 @@
this.target = group.getTarget();
}
+ public void setup() {
+ setupInvokeSuperMapping();
+ }
+
+ private void setupInvokeSuperMapping() {
+ source.forEachProgramMethod(this::redirectSuperCallsToMethod);
+ }
+
+ private void redirectSuperCallsToMethod(ProgramMethod sourceMethod) {
+ if (sourceMethod.getAccessFlags().belongsToDirectPool()) {
+ redirectSuperCallsToDirectMethod(sourceMethod);
+ } else {
+ redirectSuperCallsToVirtualMethod(sourceMethod);
+ }
+ }
+
+ private void redirectSuperCallsToDirectMethod(ProgramMethod sourceMethod) {
+ DexEncodedMethod definition = sourceMethod.getDefinition();
+ if (appView.options().canUseNestBasedAccess()
+ && source.isInSameNest(target)
+ && definition.isInstance()
+ && !definition.isInstanceInitializer()
+ && AccessControl.isMemberAccessible(sourceMethod, source, target, appView).isTrue()) {
+ lensBuilder.mapVirtualMethodToDirectInType(sourceMethod.getReference(), sourceMethod, target);
+ }
+ }
+
+ private void redirectSuperCallsToVirtualMethod(ProgramMethod sourceMethod) {
+ if (sourceMethod.getAccessFlags().isAbstract()) {
+ return;
+ }
+ if (source.isInterface()) {
+ // If we merge a default interface method from interface I to its subtype C, then we need
+ // to rewrite invocations on the form "invoke-super I.m()" to "invoke-direct C.m$I()".
+ //
+ // Unlike when we merge a class into its subclass (the else-branch below), we should *not*
+ // rewrite any invocations on the form "invoke-super J.m()" to "invoke-direct C.m$I()",
+ // if I has a supertype J. This is due to the fact that invoke-super instructions that
+ // resolve to a method on an interface never hit an implementation below that interface.
+ lensBuilder.mapVirtualMethodToDirectInType(sourceMethod.getReference(), sourceMethod, target);
+ } else {
+ // If we merge class B into class C, and class C contains an invocation super.m(), then it
+ // is insufficient to rewrite "invoke-super B.m()" to "invoke-{direct,virtual} C.m$B()" (the
+ // method C.m$B denotes the direct/virtual method that has been created in C for B.m). In
+ // particular, there might be an instruction "invoke-super A.m()" in C that resolves to B.m
+ // at runtime (A is a superclass of B), which also needs to be rewritten to
+ // "invoke-{direct,virtual} C.m$B()".
+ //
+ // We handle this by adding a mapping for [target] and all of its supertypes.
+ DexProgramClass current = target;
+ while (current != null) {
+ // Only rewrite the invoke-super call if it does not lead to a NoSuchMethodError.
+ boolean resolutionSucceeds =
+ appView
+ .appInfo()
+ .resolveMethodOnClass(current, sourceMethod.getReference())
+ .isSingleResolution();
+ if (!resolutionSucceeds) {
+ break;
+ }
+ DexMethod signatureInHolder =
+ sourceMethod.getReference().withHolder(current, dexItemFactory);
+ lensBuilder.mapVirtualMethodToDirectInType(signatureInHolder, sourceMethod, target);
+ current =
+ current.hasSuperType()
+ ? asProgramClassOrNull(appView.definitionFor(current.getSuperType()))
+ : null;
+ }
+ }
+ }
+
public void merge() {
// Merge the class [clazz] into [targetClass] by adding all methods to
// targetClass that are not currently contained.
@@ -132,51 +204,37 @@
add(directMethods, resultingDirectMethod, MethodSignatureEquivalence.get());
lensBuilder.recordMove(directMethod.getDefinition(), resultingDirectMethod);
blockRedirectionOfSuperCalls(resultingDirectMethod);
-
- // Private methods in the parent class may be targeted with invoke-super if the two
- // classes are in the same nest. Ensure such calls are mapped to invoke-direct.
- if (definition.isInstance()
- && definition.isPrivate()
- && AccessControl.isMemberAccessible(directMethod, source, target, appView)
- .isTrue()) {
- lensBuilder.mapVirtualMethodToDirectInType(
- definition.getReference(), definition, target.getType());
- }
}
});
- for (DexEncodedMethod virtualMethod : source.virtualMethods()) {
- DexEncodedMethod shadowedBy = findMethodInTarget(virtualMethod);
+ for (DexEncodedMethod abstractMethod : source.virtualMethods(DexEncodedMethod::isAbstract)) {
+ DexEncodedMethod shadowedBy = findMethodInTarget(abstractMethod);
if (shadowedBy != null) {
- if (virtualMethod.isAbstract()) {
- // Remove abstract/interface methods that are shadowed. The identity mapping below is
- // needed to ensure we correctly fixup the mapping in case the signature refers to
- // merged classes.
- lensBuilder.recordSplit(virtualMethod, shadowedBy, null, null);
+ // Remove abstract/interface methods that are shadowed. The identity mapping below is
+ // needed to ensure we correctly fixup the mapping in case the signature refers to
+ // merged classes.
+ lensBuilder.recordSplit(abstractMethod, shadowedBy, null, null);
- // The override now corresponds to the method in the parent, so unset its synthetic flag
- // if the method in the parent is not synthetic.
- if (!virtualMethod.isSyntheticMethod() && shadowedBy.isSyntheticMethod()) {
- shadowedBy.getAccessFlags().demoteFromSynthetic();
- }
- continue;
+ // The override now corresponds to the method in the parent, so unset its synthetic flag
+ // if the method in the parent is not synthetic.
+ if (!abstractMethod.isSyntheticMethod() && shadowedBy.isSyntheticMethod()) {
+ shadowedBy.getAccessFlags().demoteFromSynthetic();
}
} else {
// The method is not shadowed. If it is abstract, we can simply move it to the subclass.
- // Non-abstract methods are handled below (they cannot simply be moved to the subclass as
- // a virtual method, because they might be the target of an invoke-super instruction).
- if (virtualMethod.isAbstract()) {
- assert target.isAbstract();
- // Update the holder of [virtualMethod] using renameMethod().
- DexEncodedMethod resultingVirtualMethod =
- renameMethod(virtualMethod, availableMethodSignatures, Rename.NEVER);
- resultingVirtualMethod.setLibraryMethodOverride(virtualMethod.isLibraryMethodOverride());
- lensBuilder.recordMove(virtualMethod, resultingVirtualMethod);
- add(virtualMethods, resultingVirtualMethod, MethodSignatureEquivalence.get());
- continue;
- }
+ assert target.isAbstract();
+ // Update the holder of [virtualMethod] using renameMethod().
+ DexEncodedMethod resultingAbstractMethod =
+ renameMethod(abstractMethod, availableMethodSignatures, Rename.NEVER);
+ resultingAbstractMethod.setLibraryMethodOverride(abstractMethod.isLibraryMethodOverride());
+ lensBuilder.recordMove(abstractMethod, resultingAbstractMethod);
+ add(virtualMethods, resultingAbstractMethod, MethodSignatureEquivalence.get());
}
+ }
+ for (DexEncodedMethod virtualMethod :
+ source.virtualMethods(not(DexEncodedMethod::isAbstract))) {
+ DexEncodedMethod shadowedBy = findMethodInTarget(virtualMethod);
DexEncodedMethod resultingMethod;
if (source.isInterface()) {
// Moving a default interface method into its subtype. This method could be hit directly
@@ -210,10 +268,6 @@
resultingMethod,
MethodSignatureEquivalence.get());
- // Record that invoke-super instructions in the target class should be redirected to the
- // newly created direct method.
- redirectSuperCallsInTarget(virtualMethod);
-
DexEncodedMethod bridge = null;
DexEncodedMethod override = shadowedBy;
if (shadowedBy == null) {
@@ -468,66 +522,6 @@
type -> true);
}
- private void redirectSuperCallsInTarget(DexEncodedMethod oldTarget) {
- DexMethod oldTargetReference = oldTarget.getReference();
- if (source.isInterface()) {
- // If we merge a default interface method from interface I to its subtype C, then we need
- // to rewrite invocations on the form "invoke-super I.m()" to "invoke-direct C.m$I()".
- //
- // Unlike when we merge a class into its subclass (the else-branch below), we should *not*
- // rewrite any invocations on the form "invoke-super J.m()" to "invoke-direct C.m$I()",
- // if I has a supertype J. This is due to the fact that invoke-super instructions that
- // resolve to a method on an interface never hit an implementation below that interface.
- lensBuilder.mapVirtualMethodToDirectInType(oldTargetReference, oldTarget, target.getType());
- } else {
- // If we merge class B into class C, and class C contains an invocation super.m(), then it
- // is insufficient to rewrite "invoke-super B.m()" to "invoke-{direct,virtual} C.m$B()" (the
- // method C.m$B denotes the direct/virtual method that has been created in C for B.m). In
- // particular, there might be an instruction "invoke-super A.m()" in C that resolves to B.m
- // at runtime (A is a superclass of B), which also needs to be rewritten to
- // "invoke-{direct,virtual} C.m$B()".
- //
- // We handle this by adding a mapping for [target] and all of its supertypes.
- DexProgramClass holder = target;
- while (holder != null) {
- DexMethod signatureInHolder = oldTargetReference.withHolder(holder, dexItemFactory);
- // Only rewrite the invoke-super call if it does not lead to a NoSuchMethodError.
- boolean resolutionSucceeds =
- appView.appInfo().resolveMethodOnClass(holder, signatureInHolder).isSingleResolution();
- if (resolutionSucceeds) {
- lensBuilder.mapVirtualMethodToDirectInType(signatureInHolder, oldTarget, target.type);
- } else {
- break;
- }
-
- // Consider that A gets merged into B and B's subclass C gets merged into D. Instructions
- // on the form "invoke-super {B,C,D}.m()" in D are changed into "invoke-direct D.m$C()" by
- // the code above. However, instructions on the form "invoke-super A.m()" should also be
- // changed into "invoke-direct D.m$C()". This is achieved by also considering the classes
- // that have been merged into [holder].
- Set<DexType> mergedTypes = verticallyMergedClassesBuilder.getSourcesFor(holder);
- for (DexType type : mergedTypes) {
- DexMethod signatureInType = oldTargetReference.withHolder(type, dexItemFactory);
- // Resolution would have succeeded if the method used to be in [type], or if one of
- // its super classes declared the method.
- // TODO(b/315283244): Should not rely on lens for this. Instead precompute this before
- // merging any classes.
- boolean resolutionSucceededBeforeMerge =
- outerLensBuilder.hasMappingForSignatureInContext(holder, signatureInType)
- || appView.appInfo().lookupSuperTarget(signatureInHolder, holder, appView)
- != null;
- if (resolutionSucceededBeforeMerge) {
- lensBuilder.mapVirtualMethodToDirectInType(signatureInType, oldTarget, target.type);
- }
- }
- holder =
- holder.hasSuperType()
- ? asProgramClassOrNull(appView.definitionFor(holder.getSuperType()))
- : null;
- }
- }
- }
-
private void blockRedirectionOfSuperCalls(DexEncodedMethod method) {
// We are merging a class B into C. The methods from B are being moved into C, and then we
// subsequently rewrite the invoke-super instructions in C that hit a method in B, such that
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/ConnectedComponentVerticalClassMerger.java b/src/main/java/com/android/tools/r8/verticalclassmerging/ConnectedComponentVerticalClassMerger.java
index fdd9383..dc82f99 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/ConnectedComponentVerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/ConnectedComponentVerticalClassMerger.java
@@ -14,7 +14,7 @@
public class ConnectedComponentVerticalClassMerger {
private final AppView<AppInfoWithLiveness> appView;
- private final Collection<VerticalMergeGroup> classesToMerge;
+ private final Collection<VerticalMergeGroup> groups;
// The resulting graph lens that should be used after class merging.
private final VerticalClassMergerGraphLens.Builder lensBuilder;
@@ -26,25 +26,31 @@
VerticallyMergedClasses.builder();
ConnectedComponentVerticalClassMerger(
- AppView<AppInfoWithLiveness> appView, Collection<VerticalMergeGroup> classesToMerge) {
+ AppView<AppInfoWithLiveness> appView, Collection<VerticalMergeGroup> groups) {
this.appView = appView;
- this.classesToMerge = classesToMerge;
+ this.groups = groups;
this.lensBuilder = new VerticalClassMergerGraphLens.Builder();
}
public boolean isEmpty() {
- return classesToMerge.isEmpty();
+ return groups.isEmpty();
}
public VerticalClassMergerResult.Builder run() {
- List<VerticalMergeGroup> classesToMergeSorted =
- ListUtils.sort(classesToMerge, Comparator.comparing(group -> group.getSource().getType()));
- for (VerticalMergeGroup group : classesToMergeSorted) {
- ClassMerger classMerger =
- new ClassMerger(
- appView, lensBuilder, synthesizedBridges, verticallyMergedClassesBuilder, group);
- classMerger.merge();
- }
+ List<VerticalMergeGroup> groupsSorted =
+ ListUtils.sort(groups, Comparator.comparing(group -> group.getSource().getType()));
+ List<ClassMerger> classMergers =
+ ListUtils.map(
+ groupsSorted,
+ group ->
+ new ClassMerger(
+ appView,
+ lensBuilder,
+ synthesizedBridges,
+ verticallyMergedClassesBuilder,
+ group));
+ classMergers.forEach(ClassMerger::setup);
+ classMergers.forEach(ClassMerger::merge);
return VerticalClassMergerResult.builder(
lensBuilder, synthesizedBridges, verticallyMergedClassesBuilder);
}
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java
index a3d1257..3987216 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.graph.lens.MethodLookupResult;
import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
@@ -515,9 +516,10 @@
}
}
- public void mapVirtualMethodToDirectInType(DexMethod from, DexEncodedMethod to, DexType type) {
+ public void mapVirtualMethodToDirectInType(
+ DexMethod from, ProgramMethod to, DexProgramClass context) {
contextualSuperToImplementationInContexts
- .computeIfAbsent(type, ignoreKey(IdentityHashMap::new))
+ .computeIfAbsent(context.getType(), ignoreKey(IdentityHashMap::new))
.put(from, to.getReference());
}
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java
index bab7656..1874199 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java
@@ -35,7 +35,6 @@
import com.android.tools.r8.verticalclassmerging.policies.SameNestPolicy;
import com.android.tools.r8.verticalclassmerging.policies.SameStartupPartitionPolicy;
import com.android.tools.r8.verticalclassmerging.policies.SuccessfulVirtualMethodResolutionInTargetPolicy;
-import com.android.tools.r8.verticalclassmerging.policies.VerticalClassMergerPolicy;
import com.android.tools.r8.verticalclassmerging.policies.VerticalClassMergerPolicyWithPreprocessing;
import java.util.ArrayList;
import java.util.Collection;
@@ -113,18 +112,8 @@
protected LinkedList<VerticalMergeGroup> apply(
Policy policy, LinkedList<VerticalMergeGroup> linkedGroups, ExecutorService executorService)
throws ExecutionException {
- if (policy.isVerticalClassMergerPolicy()) {
- return apply(policy.asVerticalClassMergerPolicy(), linkedGroups);
- } else {
- assert policy.isVerticalClassMergerPolicyWithPreprocessing();
- return apply(policy.asVerticalClassMergerPolicyWithPreprocessing(), linkedGroups);
- }
- }
-
- private LinkedList<VerticalMergeGroup> apply(
- VerticalClassMergerPolicy policy, LinkedList<VerticalMergeGroup> linkedGroups) {
- linkedGroups.removeIf(group -> !policy.canMerge(group));
- return linkedGroups;
+ assert policy.isVerticalClassMergerPolicy();
+ return apply(policy.asVerticalClassMergerPolicy(), linkedGroups);
}
private <T> LinkedList<VerticalMergeGroup> apply(
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/policies/VerticalClassMergerPolicy.java b/src/main/java/com/android/tools/r8/verticalclassmerging/policies/VerticalClassMergerPolicy.java
index 49f179f..b5a6390 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/policies/VerticalClassMergerPolicy.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/policies/VerticalClassMergerPolicy.java
@@ -3,20 +3,21 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.verticalclassmerging.policies;
-import com.android.tools.r8.classmerging.Policy;
import com.android.tools.r8.verticalclassmerging.VerticalMergeGroup;
+import java.util.Collection;
-public abstract class VerticalClassMergerPolicy extends Policy {
+public abstract class VerticalClassMergerPolicy
+ extends VerticalClassMergerPolicyWithPreprocessing<Void> {
public abstract boolean canMerge(VerticalMergeGroup group);
@Override
- public boolean isVerticalClassMergerPolicy() {
- return true;
+ public final boolean canMerge(VerticalMergeGroup group, Void data) {
+ return canMerge(group);
}
@Override
- public VerticalClassMergerPolicy asVerticalClassMergerPolicy() {
- return this;
+ public final Void preprocess(Collection<VerticalMergeGroup> groups) {
+ return null;
}
}
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/policies/VerticalClassMergerPolicyWithPreprocessing.java b/src/main/java/com/android/tools/r8/verticalclassmerging/policies/VerticalClassMergerPolicyWithPreprocessing.java
index 7737ebc..5efbe75 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/policies/VerticalClassMergerPolicyWithPreprocessing.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/policies/VerticalClassMergerPolicyWithPreprocessing.java
@@ -14,13 +14,12 @@
public abstract T preprocess(Collection<VerticalMergeGroup> groups);
@Override
- public boolean isVerticalClassMergerPolicyWithPreprocessing() {
+ public boolean isVerticalClassMergerPolicy() {
return true;
}
@Override
- public VerticalClassMergerPolicyWithPreprocessing<T>
- asVerticalClassMergerPolicyWithPreprocessing() {
+ public VerticalClassMergerPolicyWithPreprocessing<T> asVerticalClassMergerPolicy() {
return this;
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugarCollectionsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugarCollectionsTest.java
new file mode 100644
index 0000000..a740261
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugarCollectionsTest.java
@@ -0,0 +1,283 @@
+// Copyright (c) 2023, 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.desugar.desugaredlibrary;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.Spliterator;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.Vector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class DesugarCollectionsTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final CompilationSpecification compilationSpecification;
+ private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+
+ @Parameters(name = "{0}, spec: {1}, {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDexRuntimesAndAllApiLevels().build(),
+ ImmutableList.of(JDK11, JDK11_PATH),
+ DEFAULT_SPECIFICATIONS);
+ }
+
+ public DesugarCollectionsTest(
+ TestParameters parameters,
+ LibraryDesugaringSpecification libraryDesugaringSpecification,
+ CompilationSpecification compilationSpecification) {
+ this.parameters = parameters;
+ this.compilationSpecification = compilationSpecification;
+ this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+ }
+
+ @Test
+ public void testDesugarCollectionsTest() throws Throwable {
+ testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(getExpectedResult());
+ }
+
+ private String getExpectedResult() {
+ List<String> result = new ArrayList<>();
+ for (int i = 0; i < 9; i++) {
+ result.add("item");
+ }
+ for (int i = 0; i < 6; i++) {
+ result.add("k v");
+ }
+ for (int i = 0; i < 6; i++) {
+ result.add("exception");
+ }
+ result.addAll(ImmutableList.of("one", "v", "k", "k=v"));
+ return StringUtils.lines(result);
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ successTest();
+ exceptionTest();
+ apiTest();
+ }
+
+ private static void apiTest() {
+ Vector<String> strings = new Vector<>();
+ strings.add("one");
+ strings.subList(0, 1).forEach(System.out::println);
+ Hashtable<String, String> table = new Hashtable<>();
+ table.put("k", "v");
+ table.values().forEach(System.out::println);
+ table.keySet().forEach(System.out::println);
+ table.entrySet().forEach(System.out::println);
+ }
+
+ private static void successTest() {
+ SortedSet<String> set = new TreeSet<>();
+ set.add("item");
+ Collections.unmodifiableCollection(set).forEach(System.out::println);
+ Collections.unmodifiableSet(set).forEach(System.out::println);
+ Collections.unmodifiableSortedSet(set).forEach(System.out::println);
+ Collections.synchronizedCollection(set).forEach(System.out::println);
+ Collections.synchronizedSet(set).forEach(System.out::println);
+ Collections.synchronizedSortedSet(set).forEach(System.out::println);
+ Collections.checkedCollection(set, String.class).forEach(System.out::println);
+ Collections.checkedSet(set, String.class).forEach(System.out::println);
+ Collections.checkedSortedSet(set, String.class).forEach(System.out::println);
+ SortedMap<String, String> map = new TreeMap<>();
+ map.put("k", "v");
+ Collections.unmodifiableMap(map).forEach((k, v) -> System.out.println(k + " " + v));
+ Collections.unmodifiableSortedMap(map).forEach((k, v) -> System.out.println(k + " " + v));
+ Collections.synchronizedMap(map).forEach((k, v) -> System.out.println(k + " " + v));
+ Collections.synchronizedSortedMap(map).forEach((k, v) -> System.out.println(k + " " + v));
+ Collections.checkedMap(map, String.class, String.class)
+ .forEach((k, v) -> System.out.println(k + " " + v));
+ Collections.checkedSortedMap(map, String.class, String.class)
+ .forEach((k, v) -> System.out.println(k + " " + v));
+ }
+
+ private static void exceptionTest() {
+ try {
+ Collections.unmodifiableCollection(new ImmutableList<>()).spliterator();
+ System.out.println("working");
+ } catch (UnsupportedOperationException e) {
+ System.out.println("exception");
+ }
+ try {
+ Collections.unmodifiableList(new ImmutableList<>()).spliterator();
+ System.out.println("working");
+ } catch (UnsupportedOperationException e) {
+ System.out.println("exception");
+ }
+ try {
+ Collections.synchronizedList(new ImmutableList<>()).spliterator();
+ System.out.println("working");
+ } catch (UnsupportedOperationException e) {
+ System.out.println("exception");
+ }
+ try {
+ Collections.synchronizedList(new ImmutableList<>()).spliterator();
+ System.out.println("working");
+ } catch (UnsupportedOperationException e) {
+ System.out.println("exception");
+ }
+ try {
+ Collections.checkedList(new ImmutableList<>(), Vector.class).spliterator();
+ System.out.println("working");
+ } catch (UnsupportedOperationException e) {
+ System.out.println("exception");
+ }
+ try {
+ Collections.checkedList(new ImmutableList<>(), Vector.class).spliterator();
+ System.out.println("working");
+ } catch (UnsupportedOperationException e) {
+ System.out.println("exception");
+ }
+ }
+
+ static class ImmutableList<T> implements List<T> {
+
+ @Override
+ public int size() {
+ return 0;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return false;
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ return null;
+ }
+
+ @Override
+ public Object[] toArray() {
+ return new Object[0];
+ }
+
+ @Override
+ public <T1> T1[] toArray(T1[] a) {
+ return null;
+ }
+
+ @Override
+ public boolean add(T t) {
+ return false;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ return false;
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> c) {
+ return false;
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends T> c) {
+ return false;
+ }
+
+ @Override
+ public boolean addAll(int index, Collection<? extends T> c) {
+ return false;
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c) {
+ return false;
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c) {
+ return false;
+ }
+
+ @Override
+ public void clear() {}
+
+ @Override
+ public T get(int index) {
+ return null;
+ }
+
+ @Override
+ public T set(int index, T element) {
+ return null;
+ }
+
+ @Override
+ public void add(int index, T element) {}
+
+ @Override
+ public T remove(int index) {
+ return null;
+ }
+
+ @Override
+ public int indexOf(Object o) {
+ return 0;
+ }
+
+ @Override
+ public int lastIndexOf(Object o) {
+ return 0;
+ }
+
+ @Override
+ public ListIterator<T> listIterator() {
+ return null;
+ }
+
+ @Override
+ public ListIterator<T> listIterator(int index) {
+ return null;
+ }
+
+ @Override
+ public List<T> subList(int fromIndex, int toIndex) {
+ return null;
+ }
+
+ @Override
+ public Spliterator<T> spliterator() {
+ throw new UnsupportedOperationException();
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/files/ArchiveWithDexTest.java b/src/test/java/com/android/tools/r8/files/ArchiveWithDexTest.java
index 0c3bd18..85b25f3 100644
--- a/src/test/java/com/android/tools/r8/files/ArchiveWithDexTest.java
+++ b/src/test/java/com/android/tools/r8/files/ArchiveWithDexTest.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.files;
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticOrigin;
+import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
@@ -11,10 +13,15 @@
import com.android.tools.r8.ArchiveProgramResourceProvider;
import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.ArchiveResourceProvider;
import com.android.tools.r8.utils.BooleanBox;
@@ -33,6 +40,8 @@
@RunWith(Parameterized.class)
public class ArchiveWithDexTest extends TestBase {
+ static final String MESSAGE = "containing both DEX and Java-bytecode content";
+
@Parameter() public TestParameters parameters;
@Parameters(name = "{0}")
@@ -41,6 +50,7 @@
}
private static Path zipWithDexAndClass;
+ private static Origin zipWithDexAndClassOrigin;
@BeforeClass
public static void createZipWitDexAndClass() throws IOException {
@@ -51,6 +61,7 @@
Files.createFile(zipContent.resolve("other.dex"));
zipWithDexAndClass = getStaticTemp().newFolder().toPath().resolve("input.zip");
ZipUtils.zip(zipWithDexAndClass, zipContent);
+ zipWithDexAndClassOrigin = new PathOrigin(zipWithDexAndClass);
}
private void checkOneDexFiles(Path archive) throws Exception {
@@ -85,6 +96,41 @@
}
@Test
+ public void testFileInputD8() {
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ testForD8(Backend.DEX)
+ .addProgramFiles(zipWithDexAndClass)
+ .setMinApi(AndroidApiLevel.B)
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertErrorsMatch(
+ allOf(
+ diagnosticMessage(containsString("input.zip")),
+ diagnosticMessage(containsString(MESSAGE)),
+ diagnosticOrigin(zipWithDexAndClassOrigin)))));
+ }
+
+ @Test
+ public void testProviderInputD8() {
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ testForD8(Backend.DEX)
+ .addProgramResourceProviders(
+ ArchiveProgramResourceProvider.fromArchive(zipWithDexAndClass))
+ .setMinApi(AndroidApiLevel.B)
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertErrorsMatch(
+ allOf(
+ // When using providers the file name is not in the message.
+ diagnosticMessage(containsString(MESSAGE)),
+ diagnosticOrigin(zipWithDexAndClassOrigin)))));
+ }
+
+ @Test
public void testR8() {
assertThrows(
CompilationFailedException.class,
@@ -97,10 +143,10 @@
.compileWithExpectedDiagnostics(
diagnostics ->
diagnostics.assertErrorsMatch(
- diagnosticMessage(
- containsString(
- "Cannot create android app from an archive containing both DEX"
- + " and Java-bytecode content.")))));
+ allOf(
+ // When using providers the file name is not in the message.
+ diagnosticMessage(containsString(MESSAGE)),
+ diagnosticOrigin(zipWithDexAndClassOrigin)))));
}
@Test
@@ -130,6 +176,19 @@
}
@Test
+ public void testRawCommandBuilderR8() throws Exception {
+ // Check that the error is wrapped and reported at the point of compiling (not arg building).
+ R8Command command =
+ R8Command.builder()
+ .addProgramResourceProvider(
+ ArchiveProgramResourceProvider.fromArchive(zipWithDexAndClass))
+ .setMinApiLevel(1)
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .build();
+ assertThrows(CompilationFailedException.class, () -> R8.run(command));
+ }
+
+ @Test
public void testR8LegacyProgramResourceProviderDontIgnoreDex() {
assertThrows(
CompilationFailedException.class,
@@ -142,8 +201,10 @@
.compileWithExpectedDiagnostics(
diagnostics ->
diagnostics.assertErrorsMatch(
- diagnosticMessage(
- containsString("containing both DEX and Java-bytecode content")))));
+ allOf(
+ // When using providers the file name is not in the message.
+ diagnosticMessage(containsString(MESSAGE)),
+ diagnosticOrigin(zipWithDexAndClassOrigin)))));
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalRuleOnMemberFieldWithKeepInitTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalRuleOnMemberFieldWithKeepInitTest.java
new file mode 100644
index 0000000..68db4f1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalRuleOnMemberFieldWithKeepInitTest.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2023, 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.shaking.ifrule;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ConditionalRuleOnMemberFieldWithKeepInitTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDefaultDexRuntime().build();
+ }
+
+ private TestParameters parameters;
+
+ public ConditionalRuleOnMemberFieldWithKeepInitTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private void testKeepRule(String keepRule, List<Class<?>> absent, List<Class<?>> present)
+ throws IOException, ExecutionException, CompilationFailedException {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(A.class, TestClass.class)
+ .addKeepRules(keepRule)
+ .addKeepMainRule(TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("42")
+ .inspect(
+ inspector -> {
+ absent.forEach(clazz -> assertThat(inspector.clazz(clazz), isAbsent()));
+ present.forEach(clazz -> assertThat(inspector.clazz(clazz), isPresent()));
+ });
+ }
+
+ @Test
+ public void testJustStarConditionalKeepClassMembers() throws Exception {
+ String keepRule = "-if class * -keepclasseswithmembers class <1> { <init>(); }";
+ testKeepRule(keepRule, ImmutableList.of(), ImmutableList.of(TestClass.class, A.class));
+ }
+
+ @Test
+ public void testJustStarConditionalKeepClass() throws Exception {
+ String keepRule = "-if class * -keep class <1> { <init>(); }";
+ testKeepRule(keepRule, ImmutableList.of(), ImmutableList.of(TestClass.class, A.class));
+ }
+
+ @Test
+ public void testStarWithMethodConditionalKeepClassMembers() throws Exception {
+ String keepRule = "-if class * { int z; } -keepclasseswithmembers class <1> { <init>(); }";
+ testKeepRule(keepRule, ImmutableList.of(), ImmutableList.of(TestClass.class, A.class));
+ }
+
+ @Test
+ public void testStarWithMethodConditionalKeepClass() throws Exception {
+ String keepRule = "-if class * { int z; } -keep class <1> { <init>(); }";
+ testKeepRule(keepRule, ImmutableList.of(), ImmutableList.of(TestClass.class, A.class));
+ }
+
+ static class A {
+ public static int z = 42;
+ }
+
+ static class TestClass {
+ public static void main(String[] args) {
+ System.out.println(A.z);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalRuleOnMemberWithKeepInitSimpleInlineTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalRuleOnMemberWithKeepInitSimpleInlineTest.java
new file mode 100644
index 0000000..f2300e3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalRuleOnMemberWithKeepInitSimpleInlineTest.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2023, 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.shaking.ifrule;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ConditionalRuleOnMemberWithKeepInitSimpleInlineTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDefaultDexRuntime().build();
+ }
+
+ private TestParameters parameters;
+
+ public ConditionalRuleOnMemberWithKeepInitSimpleInlineTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private void testKeepRule(String keepRule, List<Class<?>> absent, List<Class<?>> present)
+ throws IOException, ExecutionException, CompilationFailedException {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(A.class, TestClass.class)
+ .addKeepRules(keepRule)
+ .addKeepMainRule(TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("bar")
+ .inspect(
+ inspector -> {
+ absent.forEach(clazz -> assertThat(inspector.clazz(clazz), isAbsent()));
+ present.forEach(clazz -> assertThat(inspector.clazz(clazz), isPresent()));
+ });
+ }
+
+ @Test
+ public void testJustStarConditionalKeepClassMembers() throws Exception {
+ String keepRule = "-if class * -keepclasseswithmembers class <1> { <init>(); }";
+ testKeepRule(keepRule, ImmutableList.of(), ImmutableList.of(TestClass.class, A.class));
+ }
+
+ @Test
+ public void testJustStarConditionalKeepClass() throws Exception {
+ String keepRule = "-if class * -keep class <1> { <init>(); }";
+ testKeepRule(keepRule, ImmutableList.of(), ImmutableList.of(TestClass.class, A.class));
+ }
+
+ @Test
+ public void testStarWithMethodConditionalKeepClassMembers() throws Exception {
+ String keepRule = "-if class * { bar(); } -keepclasseswithmembers class <1> { <init>(); }";
+ testKeepRule(keepRule, ImmutableList.of(), ImmutableList.of(TestClass.class, A.class));
+ }
+
+ @Test
+ public void testStarWithMethodConditionalKeepClass() throws Exception {
+ String keepRule = "-if class * { bar(); } -keep class <1> { <init>(); }";
+ testKeepRule(keepRule, ImmutableList.of(), ImmutableList.of(TestClass.class, A.class));
+ }
+
+ static class A {
+ public static String bar() {
+ return "bar";
+ }
+ }
+
+ static class TestClass {
+ public static void main(String[] args) {
+ System.out.println(A.bar());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalRuleOnMemberWithKeepInitTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalRuleOnMemberWithKeepInitTest.java
new file mode 100644
index 0000000..38ff7d5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalRuleOnMemberWithKeepInitTest.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2023, 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.shaking.ifrule;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ConditionalRuleOnMemberWithKeepInitTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDefaultDexRuntime().build();
+ }
+
+ private TestParameters parameters;
+
+ public ConditionalRuleOnMemberWithKeepInitTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private void testKeepRule(String keepRule, List<Class<?>> absent, List<Class<?>> present)
+ throws IOException, ExecutionException, CompilationFailedException {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(A.class, TestClass.class)
+ .addKeepRules(keepRule)
+ .addKeepMainRule(TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("bar")
+ .inspect(
+ inspector -> {
+ absent.forEach(clazz -> assertThat(inspector.clazz(clazz), isAbsent()));
+ present.forEach(clazz -> assertThat(inspector.clazz(clazz), isPresent()));
+ });
+ }
+
+ @Test
+ public void testJustStarConditionalKeepClassMembers() throws Exception {
+ String keepRule = "-if class * -keepclasseswithmembers class <1> { <init>(); }";
+ // TODO(b/316100042) We should keep A here
+ testKeepRule(keepRule, ImmutableList.of(A.class), ImmutableList.of(TestClass.class));
+ }
+
+ @Test
+ public void testJustStarConditionalKeepClass() throws Exception {
+ String keepRule = "-if class * -keep class <1> { <init>(); }";
+ // TODO(b/316100042) We should keep A here
+ testKeepRule(keepRule, ImmutableList.of(A.class), ImmutableList.of(TestClass.class));
+ }
+
+ @Test
+ public void testStarWithMethodConditionalKeepClassMembers() throws Exception {
+ String keepRule = "-if class * { bar(); } -keepclasseswithmembers class <1> { <init>(); }";
+ testKeepRule(keepRule, ImmutableList.of(), ImmutableList.of(TestClass.class, A.class));
+ }
+
+ @Test
+ public void testStarWithMethodConditionalKeepClass() throws Exception {
+ String keepRule = "-if class * { bar(); } -keep class <1> { <init>(); }";
+ testKeepRule(keepRule, ImmutableList.of(), ImmutableList.of(TestClass.class, A.class));
+ }
+
+ static class A {
+ public static void bar() {
+ System.out.println("bar");
+ }
+ }
+
+ static class TestClass {
+ public static void main(String[] args) {
+ A.bar();
+ }
+ }
+}
diff --git a/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1 b/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
index 758e8f0..c5ef954 100644
--- a/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
+++ b/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
@@ -1 +1 @@
-47cfa6d72be316cdaff6e5042111a18215ac4405
\ No newline at end of file
+d50abf6c508c1332eb80f35f5631e99e2e8329b5
\ No newline at end of file