Reland "Implicitly add necessary rules for Java reflections in any R8 mode." PS1: same as original: https://r8-review.googlesource.com/c/r8/+/24260 PS4: rebase to a fix: https://r8-review.googlesource.com/c/r8/+/24424 PS6: fix remaining tests PS7: not store -identifiernamestring rules for compat Proguard config Bug: 76181966, 111619623 Change-Id: I3b5c6e99ff7626a95b655252cf2c98fd418e0e27
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java index d92de6b..8714b28 100644 --- a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java +++ b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
@@ -26,15 +26,15 @@ import com.android.tools.r8.graph.DexValue.DexValueString; import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness; import com.android.tools.r8.shaking.ProguardClassFilter; +import it.unimi.dsi.fastutil.objects.Object2BooleanMap; import java.util.Map; -import java.util.Set; class IdentifierMinifier { private final AppInfoWithLiveness appInfo; private final ProguardClassFilter adaptClassStrings; private final NamingLens lens; - private final Set<DexItem> identifierNameStrings; + private final Object2BooleanMap<DexItem> identifierNameStrings; IdentifierMinifier( AppInfoWithLiveness appInfo, ProguardClassFilter adaptClassStrings, NamingLens lens) {
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java index b0f4dc0..830391a 100644 --- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java +++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -35,17 +35,17 @@ import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness; import com.android.tools.r8.utils.InternalOptions; import com.google.common.collect.Streams; +import it.unimi.dsi.fastutil.objects.Object2BooleanMap; import java.util.Arrays; import java.util.List; import java.util.ListIterator; import java.util.Objects; -import java.util.Set; import java.util.stream.Collectors; public class IdentifierNameStringMarker { private final AppInfo appInfo; private final DexItemFactory dexItemFactory; - private final Set<DexItem> identifierNameStrings; + private final Object2BooleanMap<DexItem> identifierNameStrings; private final InternalOptions options; public IdentifierNameStringMarker(AppInfoWithLiveness appInfo, InternalOptions options) { @@ -63,7 +63,7 @@ } private void decoupleIdentifierNameStringInField(DexEncodedField encodedField) { - if (!identifierNameStrings.contains(encodedField.field)) { + if (!identifierNameStrings.containsKey(encodedField.field)) { return; } if (!encodedField.accessFlags.isStatic()) { @@ -102,22 +102,27 @@ if (instruction.isStaticPut() || instruction.isInstancePut()) { FieldInstruction fieldPut = instruction.asFieldInstruction(); DexField field = fieldPut.getField(); - if (!identifierNameStrings.contains(field)) { + if (!identifierNameStrings.containsKey(field)) { continue; } + boolean isExplicitRule = identifierNameStrings.getBoolean(field); Value in = instruction.isStaticPut() ? instruction.asStaticPut().inValue() : instruction.asInstancePut().value(); if (!in.isConstString()) { - warnUndeterminedIdentifierIfNecessary( - appInfo, options, field, originHolder, instruction, null); + if (isExplicitRule) { + warnUndeterminedIdentifierIfNecessary( + appInfo, options, field, originHolder, instruction, null); + } continue; } DexString original = in.getConstInstruction().asConstString().getValue(); DexItemBasedString itemBasedString = inferMemberOrTypeFromNameString(appInfo, original); if (itemBasedString == null) { - warnUndeterminedIdentifierIfNecessary( - appInfo, options, field, originHolder, instruction, original); + if (isExplicitRule) { + warnUndeterminedIdentifierIfNecessary( + appInfo, options, field, originHolder, instruction, original); + } continue; } // Move the cursor back to $fieldPut @@ -163,16 +168,19 @@ } else if (instruction.isInvokeMethod()) { InvokeMethod invoke = instruction.asInvokeMethod(); DexMethod invokedMethod = invoke.getInvokedMethod(); - if (!identifierNameStrings.contains(invokedMethod)) { + if (!identifierNameStrings.containsKey(invokedMethod)) { continue; } + boolean isExplicitRule = identifierNameStrings.getBoolean(invokedMethod); List<Value> ins = invoke.arguments(); Value[] changes = new Value [ins.size()]; if (isReflectionMethod(dexItemFactory, invokedMethod)) { DexItemBasedString itemBasedString = identifyIdentiferNameString(appInfo, invoke); if (itemBasedString == null) { - warnUndeterminedIdentifierIfNecessary( - appInfo, options, invokedMethod, originHolder, instruction, null); + if (isExplicitRule) { + warnUndeterminedIdentifierIfNecessary( + appInfo, options, invokedMethod, originHolder, instruction, null); + } continue; } DexType returnType = invoke.getReturnType(); @@ -217,16 +225,20 @@ for (int i = 0; i < ins.size(); i++) { Value in = ins.get(i); if (!in.isConstString()) { - warnUndeterminedIdentifierIfNecessary( - appInfo, options, invokedMethod, originHolder, instruction, null); + if (isExplicitRule) { + warnUndeterminedIdentifierIfNecessary( + appInfo, options, invokedMethod, originHolder, instruction, null); + } continue; } DexString original = in.getConstInstruction().asConstString().getValue(); DexItemBasedString itemBasedString = inferMemberOrTypeFromNameString(appInfo, original); if (itemBasedString == null) { - warnUndeterminedIdentifierIfNecessary( - appInfo, options, invokedMethod, originHolder, instruction, original); + if (isExplicitRule) { + warnUndeterminedIdentifierIfNecessary( + appInfo, options, invokedMethod, originHolder, instruction, original); + } continue; } // Move the cursor back to $invoke
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 bcd489e..420af47 100644 --- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java +++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -5,8 +5,6 @@ import static com.android.tools.r8.naming.IdentifierNameStringUtils.identifyIdentiferNameString; import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod; -import static com.android.tools.r8.naming.IdentifierNameStringUtils.warnUndeterminedIdentifierIfNecessary; -import static com.android.tools.r8.shaking.ProguardConfigurationUtils.buildIdentifierNameStringRule; import com.android.tools.r8.Diagnostic; import com.android.tools.r8.dex.IndexedItemCollection; @@ -58,6 +56,8 @@ import com.google.common.collect.Sets; import com.google.common.collect.Sets.SetView; import it.unimi.dsi.fastutil.ints.Int2ReferenceMap; +import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap; +import it.unimi.dsi.fastutil.objects.Object2BooleanMap; import it.unimi.dsi.fastutil.objects.Reference2IntMap; import java.util.ArrayDeque; import java.util.Collection; @@ -187,10 +187,9 @@ private final Queue<Action> proguardCompatibilityWorkList = Queues.newArrayDeque(); /** - * A set of methods that need code inspection for Proguard compatibility rules. + * A set of methods that need code inspection for Java reflection in use. */ - private final Set<DexEncodedMethod> pendingProguardReflectiveCompatibility = - Sets.newLinkedHashSet(); + private final Set<DexEncodedMethod> pendingReflectiveUses = Sets.newLinkedHashSet(); /** * A cache for DexMethod that have been marked reachable. @@ -314,13 +313,10 @@ boolean registerInvokeVirtual(DexMethod method, KeepReason keepReason) { if (appInfo.dexItemFactory.classMethods.isReflectiveMemberLookup(method)) { - if (forceProguardCompatibility) { - // TODO(b/76181966): whether or not add this rule in normal mode. - if (identifierNameStrings.add(method) && compatibility != null) { - compatibility.addRule(buildIdentifierNameStringRule(method)); - } - pendingProguardReflectiveCompatibility.add(currentMethod); - } + // Implicitly add -identifiernamestring rule for the Java reflection in use. + identifierNameStrings.add(method); + // Revisit the current method to implicitly add -keep rule for items with reflective access. + pendingReflectiveUses.add(currentMethod); } if (!registerItemWithTarget(virtualInvokes, method)) { return false; @@ -356,13 +352,10 @@ boolean registerInvokeStatic(DexMethod method, KeepReason keepReason) { if (method == appInfo.dexItemFactory.classMethods.forName || appInfo.dexItemFactory.atomicFieldUpdaterMethods.isFieldUpdater(method)) { - if (forceProguardCompatibility) { - // TODO(b/76181966): whether or not add this rule in normal mode. - if (identifierNameStrings.add(method) && compatibility != null) { - compatibility.addRule(buildIdentifierNameStringRule(method)); - } - pendingProguardReflectiveCompatibility.add(currentMethod); - } + // Implicitly add -identifiernamestring rule for the Java reflection in use. + identifierNameStrings.add(method); + // Revisit the current method to implicitly add -keep rule for items with reflective access. + pendingReflectiveUses.add(currentMethod); } if (!registerItemWithTarget(staticInvokes, method)) { return false; @@ -1279,15 +1272,15 @@ } // Continue fix-point processing while there are additional work items to ensure - // Proguard compatibility. + // items that are passed to Java reflections are traced. if (proguardCompatibilityWorkList.isEmpty() - && pendingProguardReflectiveCompatibility.isEmpty()) { + && pendingReflectiveUses.isEmpty()) { break; } - pendingProguardReflectiveCompatibility.forEach(this::handleProguardReflectiveBehavior); + pendingReflectiveUses.forEach(this::handleReflectiveBehavior); workList.addAll(proguardCompatibilityWorkList); proguardCompatibilityWorkList.clear(); - pendingProguardReflectiveCompatibility.clear(); + pendingReflectiveUses.clear(); } if (Log.ENABLED) { Set<DexEncodedMethod> allLive = Sets.newIdentityHashSet(); @@ -1484,15 +1477,15 @@ Action.markMethodLive(method, KeepReason.dueToProguardCompatibilityKeepRule(rule))); } - private void handleProguardReflectiveBehavior(DexEncodedMethod method) { + private void handleReflectiveBehavior(DexEncodedMethod method) { DexType originHolder = method.method.holder; Origin origin = appInfo.originFor(originHolder); IRCode code = method.buildIR(appInfo, graphLense, options, origin); code.instructionIterator().forEachRemaining(instr -> - handleProguardReflectiveBehavior(instr, originHolder)); + handleReflectiveBehavior(instr, originHolder)); } - private void handleProguardReflectiveBehavior(Instruction instruction, DexType originHolder) { + private void handleReflectiveBehavior(Instruction instruction, DexType originHolder) { if (!instruction.isInvokeMethod()) { return; } @@ -1503,8 +1496,6 @@ } DexItemBasedString itemBasedString = identifyIdentiferNameString(appInfo, invoke); if (itemBasedString == null) { - warnUndeterminedIdentifierIfNecessary( - appInfo, options, invokedMethod, originHolder, instruction, null); return; } if (itemBasedString.basedOn instanceof DexType) { @@ -1692,8 +1683,10 @@ public final Set<DexItem> neverInline; /** * All items with -identifiernamestring rule. + * Bound boolean value indicates the rule is explicitly specified by users (<code>true</code>) + * or not, i.e., implicitly added by R8 (<code>false</code>). */ - public final Set<DexItem> identifierNameStrings; + public final Object2BooleanMap<DexItem> identifierNameStrings; /** * Set of fields that have been identified as proto-lite fields by the * {@link ProtoLiteExtension}. @@ -1750,8 +1743,8 @@ this.alwaysInline = enqueuer.rootSet.alwaysInline; this.forceInline = enqueuer.rootSet.forceInline; this.neverInline = enqueuer.rootSet.neverInline; - this.identifierNameStrings = - Sets.union(enqueuer.rootSet.identifierNameStrings, enqueuer.identifierNameStrings); + this.identifierNameStrings = joinIdentifierNameStrings( + enqueuer.rootSet.identifierNameStrings, enqueuer.identifierNameStrings); this.protoLiteFields = enqueuer.protoLiteFields; this.prunedTypes = Collections.emptySet(); this.switchMaps = Collections.emptyMap(); @@ -1922,6 +1915,18 @@ .collect(ImmutableSortedSet.toImmutableSortedSet(PresortedComparable::slowCompare)); } + private Object2BooleanMap<DexItem> joinIdentifierNameStrings( + Set<DexItem> explicit, Set<DexItem> implicit) { + Object2BooleanMap<DexItem> result = new Object2BooleanArrayMap<>(); + for (DexItem e : explicit) { + result.putIfAbsent(e, true); + } + for (DexItem i : implicit) { + result.putIfAbsent(i, false); + } + return result; + } + private <T extends PresortedComparable<T>> SortedSet<T> toSortedDescriptorSet( Set<? extends KeyedDexItem<T>> set) { ImmutableSortedSet.Builder<T> builder = @@ -2031,6 +2036,32 @@ return builder.build(); } + private static Object2BooleanMap<DexItem> rewriteMixedItemsConservatively( + Object2BooleanMap<DexItem> original, GraphLense lense) { + Object2BooleanMap<DexItem> result = new Object2BooleanArrayMap<>(); + for (Object2BooleanMap.Entry<DexItem> entry : original.object2BooleanEntrySet()) { + DexItem item = entry.getKey(); + // TODO(b/67934123) There should be a common interface to perform the dispatch. + if (item instanceof DexType) { + result.put(lense.lookupType((DexType) item), entry.getBooleanValue()); + } else if (item instanceof DexMethod) { + DexMethod method = (DexMethod) item; + if (lense.isContextFreeForMethod(method)) { + result.put(lense.lookupMethod(method), entry.getBooleanValue()); + } else { + for (DexMethod candidate: lense.lookupMethodInAllContexts(method)) { + result.put(candidate, entry.getBooleanValue()); + } + } + } else if (item instanceof DexField) { + result.put(lense.lookupField((DexField) item), entry.getBooleanValue()); + } else { + throw new Unreachable(); + } + } + return result; + } + private static <T> Set<T> mergeSets(Collection<T> first, Collection<T> second) { ImmutableSet.Builder<T> builder = ImmutableSet.builder(); builder.addAll(first);
diff --git a/src/test/examples/identifiernamestring/keep-rules-3.txt b/src/test/examples/identifiernamestring/keep-rules-3.txt index aa23772..1c69314 100644 --- a/src/test/examples/identifiernamestring/keep-rules-3.txt +++ b/src/test/examples/identifiernamestring/keep-rules-3.txt
@@ -11,7 +11,7 @@ -dontshrink --identifiernamestring class * { +-identifiernamestring class **.R { static java.lang.reflect.Field *(java.lang.Class, java.lang.String); static java.lang.reflect.Method *(java.lang.Class, java.lang.String, java.lang.Class[]); }
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java index a9ee851..73cdaaf 100644 --- a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java +++ b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
@@ -142,7 +142,7 @@ FoundMethodSubject foundMain = (FoundMethodSubject) main; verifyPresenceOfConstString(foundMain); int renamedYetFoundIdentifierCount = countRenamedClassIdentifier(inspector, foundMain); - assertEquals(0, renamedYetFoundIdentifierCount); + assertEquals(4, renamedYetFoundIdentifierCount); ClassSubject aClass = inspector.clazz("adaptclassstrings.A"); MethodSubject bar = aClass.method("void", "bar", ImmutableList.of()); @@ -165,7 +165,7 @@ FoundMethodSubject foundMain = (FoundMethodSubject) main; verifyPresenceOfConstString(foundMain); int renamedYetFoundIdentifierCount = countRenamedClassIdentifier(inspector, foundMain); - assertEquals(0, renamedYetFoundIdentifierCount); + assertEquals(4, renamedYetFoundIdentifierCount); ClassSubject aClass = inspector.clazz("adaptclassstrings.A"); MethodSubject bar = aClass.method("void", "bar", ImmutableList.of()); @@ -231,7 +231,7 @@ FoundMethodSubject foundMain = (FoundMethodSubject) main; verifyPresenceOfConstString(foundMain); int renamedYetFoundIdentifierCount = countRenamedClassIdentifier(inspector, foundMain); - assertEquals(0, renamedYetFoundIdentifierCount); + assertEquals(1, renamedYetFoundIdentifierCount); ClassSubject aClass = inspector.clazz("identifiernamestring.A"); MethodSubject aInit = @@ -255,7 +255,7 @@ FoundMethodSubject foundMain = (FoundMethodSubject) main; verifyPresenceOfConstString(foundMain); int renamedYetFoundIdentifierCount = countRenamedClassIdentifier(inspector, foundMain); - assertEquals(1, renamedYetFoundIdentifierCount); + assertEquals(2, renamedYetFoundIdentifierCount); ClassSubject aClass = inspector.clazz("identifiernamestring.A"); MethodSubject aInit = @@ -271,7 +271,7 @@ assertEquals(2, renamedYetFoundIdentifierCount); } - // With -identifiernamestring for reflective methods + // With -identifiernamestring for reflective methods in testing class R. private static void test2_rule3(CodeInspector inspector) { ClassSubject mainClass = inspector.clazz("identifiernamestring.Main"); MethodSubject main = mainClass.method(CodeInspector.MAIN);
diff --git a/src/test/java/com/android/tools/r8/naming/WarnReflectiveAccessTest.java b/src/test/java/com/android/tools/r8/naming/WarnReflectiveAccessTest.java index 829cdd0..49ba186 100644 --- a/src/test/java/com/android/tools/r8/naming/WarnReflectiveAccessTest.java +++ b/src/test/java/com/android/tools/r8/naming/WarnReflectiveAccessTest.java
@@ -9,6 +9,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import com.android.tools.r8.DiagnosticsChecker; import com.android.tools.r8.OutputMode; @@ -72,15 +73,17 @@ byte[][] classes, Class main, Path out, + boolean explicitRule, boolean enableMinification, boolean forceProguardCompatibility) throws Exception { String minificationModifier = enableMinification ? ",allowobfuscation " : " "; - String reflectionRules = forceProguardCompatibility ? "" - : "-identifiernamestring class java.lang.Class {\n" + String reflectionRules = explicitRule + ? "-identifiernamestring class java.lang.Class {\n" + "static java.lang.Class forName(java.lang.String);\n" + "java.lang.reflect.Method getDeclaredMethod(java.lang.String, java.lang.Class[]);\n" - + "}\n"; + + "}\n" + : ""; R8Command.Builder commandBuilder = R8Command.builder(reporter) .addProguardConfiguration( @@ -102,7 +105,8 @@ }); } - private void reflectionWithBuildter( + private void reflectionWithBuilder( + boolean explicitRule, boolean enableMinification, boolean forceProguardCompatibility) throws Exception { byte[][] classes = { @@ -110,7 +114,7 @@ }; Path out = temp.getRoot().toPath(); AndroidApp processedApp = runR8(classes, WarnReflectiveAccessTestMain.class, out, - enableMinification, forceProguardCompatibility); + explicitRule, enableMinification, forceProguardCompatibility); String main = WarnReflectiveAccessTestMain.class.getCanonicalName(); CodeInspector codeInspector = new CodeInspector(processedApp); @@ -133,35 +137,59 @@ } @Test - public void test_minification_forceProguardCompatibility() throws Exception { - reflectionWithBuildter(true, true); + public void test_explicit_minification_forceProguardCompatibility() throws Exception { + reflectionWithBuilder(true, true, true); assertFalse(handler.warnings.isEmpty()); - DiagnosticsChecker.checkDiagnostic(handler.warnings.get(0), (Path) null, 54, 1, - "Cannot determine", "getDeclaredMethod", "resolution failure"); - } - - @Test - public void test_noMinification_forceProguardCompatibility() throws Exception { - reflectionWithBuildter(false, true); - assertFalse(handler.warnings.isEmpty()); - DiagnosticsChecker.checkDiagnostic(handler.warnings.get(0), (Path) null, 54, 1, - "Cannot determine", "getDeclaredMethod", "resolution failure"); - } - - @Test - public void test_minification_R8() throws Exception { - reflectionWithBuildter(true, false); - assertFalse(handler.warnings.isEmpty()); - DiagnosticsChecker.checkDiagnostic(handler.warnings.get(0), (Path) null, 54, 1, + DiagnosticsChecker.checkDiagnostic(handler.warnings.get(0), (Path) null, 55, 1, "Cannot determine", "getDeclaredMethod", "-identifiernamestring", "resolution failure"); } @Test - public void test_noMinification_R8() throws Exception { - reflectionWithBuildter(false, false); + public void test_explicit_noMinification_forceProguardCompatibility() throws Exception { + reflectionWithBuilder(true, false, true); assertFalse(handler.warnings.isEmpty()); - DiagnosticsChecker.checkDiagnostic(handler.warnings.get(0), (Path) null, 54, 1, + DiagnosticsChecker.checkDiagnostic(handler.warnings.get(0), (Path) null, 55, 1, "Cannot determine", "getDeclaredMethod", "-identifiernamestring", "resolution failure"); } + @Test + public void test_explicit_minification_R8() throws Exception { + reflectionWithBuilder(true, true, false); + assertFalse(handler.warnings.isEmpty()); + DiagnosticsChecker.checkDiagnostic(handler.warnings.get(0), (Path) null, 55, 1, + "Cannot determine", "getDeclaredMethod", "-identifiernamestring", "resolution failure"); + } + + @Test + public void test_explicit_noMinification_R8() throws Exception { + reflectionWithBuilder(true, false, false); + assertFalse(handler.warnings.isEmpty()); + DiagnosticsChecker.checkDiagnostic(handler.warnings.get(0), (Path) null, 55, 1, + "Cannot determine", "getDeclaredMethod", "-identifiernamestring", "resolution failure"); + } + + @Test + public void test_implicit_minification_forceProguardCompatibility() throws Exception { + reflectionWithBuilder(false, true, true); + assertTrue(handler.warnings.isEmpty()); + } + + @Test + public void test_implicit_noMinification_forceProguardCompatibility() throws Exception { + reflectionWithBuilder(false, false, true); + assertTrue(handler.warnings.isEmpty()); + } + + @Test + public void test_implicit_minification_R8() throws Exception { + reflectionWithBuilder(false, true, false); + assertTrue(handler.warnings.isEmpty()); + } + + @Test + public void test_implicit_noMinification_R8() throws Exception { + reflectionWithBuilder(false, false, false); + assertTrue(handler.warnings.isEmpty()); + } + }
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java index 51baed2..9cf0a85 100644 --- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java +++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -23,7 +23,6 @@ import com.android.tools.r8.shaking.ProguardConfiguration; import com.android.tools.r8.shaking.ProguardConfigurationParser; import com.android.tools.r8.shaking.ProguardConfigurationRule; -import com.android.tools.r8.shaking.ProguardIdentifierNameStringRule; import com.android.tools.r8.shaking.ProguardKeepAttributes; import com.android.tools.r8.shaking.ProguardKeepRule; import com.android.tools.r8.shaking.ProguardKeepRuleType; @@ -261,7 +260,7 @@ assertTrue(inspector.clazz(getJavacGeneratedClassName(mainClass)).isPresent()); forNameClasses.forEach(clazz -> { ClassSubject subject = inspector.clazz(getJavacGeneratedClassName(clazz)); - assertEquals(forceProguardCompatibility, subject.isPresent()); + assertTrue(subject.isPresent()); assertEquals(subject.isPresent() && allowObfuscation, subject.isRenamed()); }); @@ -273,8 +272,10 @@ ProguardConfiguration configuration = parser.getConfigRawForTesting(); if (forceProguardCompatibility) { List<ProguardConfigurationRule> rules = configuration.getRules(); - assertEquals(3, rules.size()); - Iterables.filter(rules, ProguardKeepRule.class).forEach(rule -> { + assertEquals(2, rules.size()); + for (ProguardConfigurationRule r : rules) { + assertTrue(r instanceof ProguardKeepRule); + ProguardKeepRule rule = (ProguardKeepRule) r; assertEquals(ProguardKeepRuleType.KEEP, rule.getType()); assertTrue(rule.getModifiers().allowsObfuscation); assertTrue(rule.getModifiers().allowsOptimization); @@ -292,18 +293,7 @@ assertEquals(1, memberRules.size()); assertEquals(ProguardMemberType.INIT, memberRules.iterator().next().getRuleType()); } - }); - Iterables.filter(rules, ProguardIdentifierNameStringRule.class).forEach(rule -> { - List<ProguardMemberRule> memberRules = rule.getMemberRules(); - ProguardClassNameList classNames = rule.getClassNames(); - assertEquals(1, classNames.size()); - DexType type = classNames.asSpecificDexTypes().get(0); - assertEquals("java.lang.Class", type.toSourceString()); - assertEquals(1, memberRules.size()); - ProguardMemberRule memberRule = memberRules.iterator().next(); - assertEquals(ProguardMemberType.METHOD, memberRule.getRuleType()); - assertEquals("forName", memberRule.getName().toString()); - }); + } } else { assertEquals(0, configuration.getRules().size()); } @@ -367,11 +357,11 @@ assertTrue(classSubject.isPresent()); assertEquals(allowObfuscation, classSubject.isRenamed()); FieldSubject foo = classSubject.field("java.lang.String", "foo"); - assertEquals(forceProguardCompatibility, foo.isPresent()); + assertTrue(foo.isPresent()); assertEquals(foo.isPresent() && allowObfuscation, foo.isRenamed()); MethodSubject bar = classSubject.method("java.lang.String", "bar", ImmutableList.of("java.lang.String")); - assertEquals(forceProguardCompatibility, bar.isPresent()); + assertTrue(bar.isPresent()); assertEquals(bar.isPresent() && allowObfuscation, bar.isRenamed()); // Check the Proguard compatibility rules generated. @@ -382,8 +372,10 @@ ProguardConfiguration configuration = parser.getConfigRawForTesting(); if (forceProguardCompatibility) { List<ProguardConfigurationRule> rules = configuration.getRules(); - assertEquals(4, rules.size()); - Iterables.filter(rules, ProguardKeepRule.class).forEach(rule -> { + assertEquals(2, rules.size()); + for (ProguardConfigurationRule r : rules) { + assertTrue(r instanceof ProguardKeepRule); + ProguardKeepRule rule = (ProguardKeepRule) r; assertEquals(ProguardKeepRuleType.KEEP_CLASS_MEMBERS, rule.getType()); assertTrue(rule.getModifiers().allowsObfuscation); assertTrue(rule.getModifiers().allowsOptimization); @@ -400,18 +392,7 @@ assertEquals(ProguardMemberType.METHOD, memberRule.getRuleType()); assertTrue(memberRule.getName().matches("bar")); } - }); - Iterables.filter(rules, ProguardIdentifierNameStringRule.class).forEach(rule -> { - List<ProguardMemberRule> memberRules = rule.getMemberRules(); - ProguardClassNameList classNames = rule.getClassNames(); - assertEquals(1, classNames.size()); - DexType type = classNames.asSpecificDexTypes().get(0); - assertEquals("java.lang.Class", type.toSourceString()); - assertEquals(1, memberRules.size()); - ProguardMemberRule memberRule = memberRules.iterator().next(); - assertEquals(ProguardMemberType.METHOD, memberRule.getRuleType()); - assertTrue(memberRule.getName().toString().startsWith("getDeclared")); - }); + } } else { assertEquals(0, configuration.getRules().size()); } @@ -480,13 +461,13 @@ assertTrue(classSubject.isPresent()); assertEquals(allowObfuscation, classSubject.isRenamed()); FieldSubject f = classSubject.field("int", "intField"); - assertEquals(forceProguardCompatibility, f.isPresent()); + assertTrue(f.isPresent()); assertEquals(f.isPresent() && allowObfuscation, f.isRenamed()); f = classSubject.field("long", "longField"); - assertEquals(forceProguardCompatibility, f.isPresent()); + assertTrue(f.isPresent()); assertEquals(f.isPresent() && allowObfuscation, f.isRenamed()); f = classSubject.field("java.lang.Object", "objField"); - assertEquals(forceProguardCompatibility, f.isPresent()); + assertTrue(f.isPresent()); assertEquals(f.isPresent() && allowObfuscation, f.isRenamed()); // Check the Proguard compatibility rules generated. @@ -497,9 +478,11 @@ ProguardConfiguration configuration = parser.getConfigRawForTesting(); if (forceProguardCompatibility) { List<ProguardConfigurationRule> rules = configuration.getRules(); - assertEquals(6, rules.size()); + assertEquals(3, rules.size()); Object2BooleanMap<String> keptFields = new Object2BooleanArrayMap<>(); - Iterables.filter(rules, ProguardKeepRule.class).forEach(rule -> { + for (ProguardConfigurationRule r : rules) { + assertTrue(r instanceof ProguardKeepRule); + ProguardKeepRule rule = (ProguardKeepRule) r; assertEquals(ProguardKeepRuleType.KEEP_CLASS_MEMBERS, rule.getType()); assertTrue(rule.getModifiers().allowsObfuscation); assertTrue(rule.getModifiers().allowsOptimization); @@ -512,23 +495,11 @@ ProguardMemberRule memberRule = memberRules.iterator().next(); assertEquals(ProguardMemberType.FIELD, memberRule.getRuleType()); keptFields.put(memberRule.getName().toString(), true); - }); + } assertEquals(3, keptFields.size()); assertTrue(keptFields.containsKey("intField")); assertTrue(keptFields.containsKey("longField")); assertTrue(keptFields.containsKey("objField")); - Iterables.filter(rules, ProguardIdentifierNameStringRule.class).forEach(rule -> { - List<ProguardMemberRule> memberRules = rule.getMemberRules(); - ProguardClassNameList classNames = rule.getClassNames(); - assertEquals(1, classNames.size()); - DexType type = classNames.asSpecificDexTypes().get(0); - assertTrue(type.toSourceString().startsWith("java.util.concurrent.atomic.Atomic")); - assertTrue(type.toSourceString().endsWith("FieldUpdater")); - assertEquals(1, memberRules.size()); - ProguardMemberRule memberRule = memberRules.iterator().next(); - assertEquals(ProguardMemberType.METHOD, memberRule.getRuleType()); - assertEquals("newUpdater", memberRule.getName().toString()); - }); } else { assertEquals(0, configuration.getRules().size()); }