Merge "Support for -if rules in presence of access relaxation"
diff --git a/src/main/java/com/android/tools/r8/graph/AccessFlags.java b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
index afbb13f..ebeae02 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
@@ -9,7 +9,7 @@
import java.util.function.BooleanSupplier;
/** Access flags common to classes, methods and fields. */
-public abstract class AccessFlags {
+public abstract class AccessFlags<T extends AccessFlags<T>> {
protected static final int BASE_FLAGS
= Constants.ACC_PUBLIC
@@ -46,26 +46,43 @@
}
protected int flags;
+ protected boolean isPublicized = false;
protected AccessFlags(int flags) {
this.flags = flags;
}
+ public abstract T copy();
+
+ public abstract T self();
+
+ public int materialize() {
+ if (isPromotedToPublic()) {
+ return ((flags | Constants.ACC_PUBLIC) & ~Constants.ACC_PROTECTED) & ~Constants.ACC_PRIVATE;
+ }
+ return flags;
+ }
+
public abstract int getAsCfAccessFlags();
public abstract int getAsDexAccessFlags();
+ public final int getOriginalCfAccessFlags() {
+ return flags;
+ }
+
@Override
- public boolean equals(Object other) {
- if (other instanceof AccessFlags) {
- return flags == ((AccessFlags) other).flags;
+ public boolean equals(Object object) {
+ if (object instanceof AccessFlags) {
+ AccessFlags other = (AccessFlags) object;
+ return flags == other.flags && isPublicized == other.isPublicized;
}
return false;
}
@Override
public int hashCode() {
- return flags;
+ return (flags << 1) | (isPublicized ? 1 : 0);
}
public boolean isMoreVisibleThan(AccessFlags other) {
@@ -92,7 +109,7 @@
}
public boolean isPublic() {
- return isSet(Constants.ACC_PUBLIC);
+ return isSet(Constants.ACC_PUBLIC) || isPromotedToPublic();
}
public void setPublic() {
@@ -105,7 +122,7 @@
}
public boolean isPrivate() {
- return isSet(Constants.ACC_PRIVATE);
+ return isSet(Constants.ACC_PRIVATE) && !isPromotedToPublic();
}
public void setPrivate() {
@@ -118,7 +135,7 @@
}
public boolean isProtected() {
- return isSet(Constants.ACC_PROTECTED);
+ return isSet(Constants.ACC_PROTECTED) && !isPromotedToPublic();
}
public void setProtected() {
@@ -162,10 +179,21 @@
unset(Constants.ACC_SYNTHETIC);
}
+ public boolean isPromotedToPublic() {
+ return isPublicized;
+ }
+
+ public T setPromotedToPublic(boolean isPublicized) {
+ this.isPublicized = isPublicized;
+ return self();
+ }
+
public void promoteToPublic() {
- unsetProtected();
- unsetPrivate();
- setPublic();
+ isPublicized = true;
+ }
+
+ public void unsetPromotedToPublic() {
+ isPublicized = false;
}
protected boolean isSet(int flag) {
diff --git a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
index 44d6726..6c7caf9 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassAccessFlags.java
@@ -8,7 +8,7 @@
import java.util.List;
import java.util.function.BooleanSupplier;
-public class ClassAccessFlags extends AccessFlags {
+public class ClassAccessFlags extends AccessFlags<ClassAccessFlags> {
// List of valid flags for both DEX and Java.
private static final int SHARED_FLAGS
@@ -68,24 +68,30 @@
return new ClassAccessFlags(access & CF_FLAGS);
}
+ @Override
public ClassAccessFlags copy() {
- return new ClassAccessFlags(flags);
+ return new ClassAccessFlags(flags).setPromotedToPublic(isPromotedToPublic());
+ }
+
+ @Override
+ public ClassAccessFlags self() {
+ return this;
}
@Override
public int getAsDexAccessFlags() {
// We unset the super flag here, as it is meaningless in DEX. Furthermore, we add missing
// abstract to interfaces to work around a javac bug when generating package-info classes.
+ int flags = materialize() & ~Constants.ACC_SUPER;
if (isInterface()) {
- return (flags & ~Constants.ACC_SUPER) | Constants.ACC_ABSTRACT;
- } else {
- return flags & ~Constants.ACC_SUPER;
+ return flags | Constants.ACC_ABSTRACT;
}
+ return flags;
}
@Override
public int getAsCfAccessFlags() {
- return flags;
+ return materialize();
}
/**
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 46738e2..79ef9d4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -211,7 +211,7 @@
public boolean isPublicized() {
checkIfObsolete();
- return optimizationInfo.isPublicized();
+ return accessFlags.isPromotedToPublic();
}
public boolean isPublicMethod() {
@@ -889,11 +889,6 @@
}
@Override
- public boolean isPublicized() {
- return NOT_PUBLICZED;
- }
-
- @Override
public boolean isInitializerEnablingJavaAssertions() {
return UNKNOWN_INITIALIZER_ENABLING_JAVA_ASSERTIONS;
}
@@ -1044,11 +1039,6 @@
}
@Override
- public boolean isPublicized() {
- return publicized;
- }
-
- @Override
public boolean isInitializerEnablingJavaAssertions() {
return initializerEnablingJavaAssertions;
}
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java b/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java
index b56e60f..e940269 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java
@@ -8,7 +8,7 @@
import java.util.List;
import java.util.function.BooleanSupplier;
-public class FieldAccessFlags extends AccessFlags {
+public class FieldAccessFlags extends AccessFlags<FieldAccessFlags> {
private static final int FLAGS
= AccessFlags.BASE_FLAGS
@@ -40,8 +40,14 @@
super(flags);
}
+ @Override
public FieldAccessFlags copy() {
- return new FieldAccessFlags(flags);
+ return new FieldAccessFlags(flags).setPromotedToPublic(isPromotedToPublic());
+ }
+
+ @Override
+ public FieldAccessFlags self() {
+ return this;
}
public static FieldAccessFlags fromSharedAccessFlags(int access) {
@@ -59,12 +65,12 @@
@Override
public int getAsCfAccessFlags() {
- return flags;
+ return materialize();
}
@Override
public int getAsDexAccessFlags() {
- return flags;
+ return materialize();
}
public boolean isVolatile() {
diff --git a/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java b/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
index 37a6fac..ca507b7 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodAccessFlags.java
@@ -8,7 +8,7 @@
import java.util.List;
import java.util.function.BooleanSupplier;
-public class MethodAccessFlags extends AccessFlags {
+public class MethodAccessFlags extends AccessFlags<MethodAccessFlags> {
private static final int SHARED_FLAGS
= AccessFlags.BASE_FLAGS
@@ -57,8 +57,14 @@
super(flags);
}
+ @Override
public MethodAccessFlags copy() {
- return new MethodAccessFlags(flags);
+ return new MethodAccessFlags(flags).setPromotedToPublic(isPromotedToPublic());
+ }
+
+ @Override
+ public MethodAccessFlags self() {
+ return this;
}
public static MethodAccessFlags fromSharedAccessFlags(int access, boolean isConstructor) {
@@ -88,12 +94,12 @@
copy.unsetSynchronized();
copy.setDeclaredSynchronized();
}
- return copy.flags;
+ return copy.materialize();
}
@Override
public int getAsCfAccessFlags() {
- return flags & ~Constants.ACC_CONSTRUCTOR;
+ return materialize() & ~Constants.ACC_CONSTRUCTOR;
}
public boolean isSynchronized() {
diff --git a/src/main/java/com/android/tools/r8/graph/OptimizationInfo.java b/src/main/java/com/android/tools/r8/graph/OptimizationInfo.java
index 4fc81d0..1a58eba 100644
--- a/src/main/java/com/android/tools/r8/graph/OptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/OptimizationInfo.java
@@ -41,8 +41,6 @@
long getReturnedConstant();
- boolean isPublicized();
-
boolean forceInline();
boolean neverInline();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
index 605fdf9..8469004 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
@@ -53,7 +53,7 @@
DexEncodedMethod mainMethod = null;
for (DexEncodedMethod method : lambda.virtualMethods()) {
- if (method.accessFlags.equals(MAIN_METHOD_FLAGS)) {
+ if (method.accessFlags.materialize() == MAIN_METHOD_FLAGS.materialize()) {
if (mainMethod != null) {
throw new LambdaStructureError("more than one main method found");
}
@@ -208,7 +208,7 @@
static <T extends AccessFlags> void checkAccessFlags(
String message, T actual, T... expected) throws LambdaStructureError {
for (T flag : expected) {
- if (flag.equals(actual)) {
+ if (flag.materialize() == actual.materialize()) {
return;
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
index d39f81b..b59e3e9 100644
--- a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
@@ -7,36 +7,32 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.ir.optimize.MethodPoolCollection;
import com.android.tools.r8.optimize.PublicizerLense.PublicizedLenseBuilder;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
-import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.Timing;
-import com.google.common.base.Equivalence;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
public final class ClassAndMemberPublicizer {
+
private final DexApplication application;
private final AppView appView;
private final RootSet rootSet;
- private final PublicizedLenseBuilder lenseBuilder;
-
- private final Equivalence<DexMethod> equivalence = MethodSignatureEquivalence.get();
private final MethodPoolCollection methodPoolCollection;
+ private final PublicizedLenseBuilder lenseBuilder = PublicizerLense.createBuilder();
+
private ClassAndMemberPublicizer(DexApplication application, AppView appView, RootSet rootSet) {
this.application = application;
this.appView = appView;
this.methodPoolCollection = new MethodPoolCollection(application);
this.rootSet = rootSet;
- lenseBuilder = PublicizerLense.createBuilder();
}
/**
@@ -99,15 +95,13 @@
}
if (!accessFlags.isPrivate()) {
- accessFlags.unsetProtected();
- accessFlags.setPublic();
+ accessFlags.promoteToPublic();
return false;
}
assert accessFlags.isPrivate();
if (appView.dexItemFactory().isConstructor(encodedMethod.method)) {
- accessFlags.unsetPrivate();
- accessFlags.setPublic();
+ accessFlags.promoteToPublic();
return false;
}
@@ -134,9 +128,8 @@
return false;
}
lenseBuilder.add(encodedMethod.method);
- accessFlags.unsetPrivate();
accessFlags.setFinal();
- accessFlags.setPublic();
+ accessFlags.promoteToPublic();
// Although the current method became public, it surely has the single virtual target.
encodedMethod.method.setSingleVirtualMethodCache(
encodedMethod.method.getHolder(), encodedMethod);
@@ -148,8 +141,7 @@
// even though JLS prevents from declaring static method in derived class if
// an instance method with same signature exists in superclass, JVM actually
// does not take into account access of the static methods.
- accessFlags.unsetPrivate();
- accessFlags.setPublic();
+ accessFlags.promoteToPublic();
return false;
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/PublicizerLense.java b/src/main/java/com/android/tools/r8/optimize/PublicizerLense.java
index b11393a..e51b38d 100644
--- a/src/main/java/com/android/tools/r8/optimize/PublicizerLense.java
+++ b/src/main/java/com/android/tools/r8/optimize/PublicizerLense.java
@@ -18,7 +18,7 @@
private final AppView appView;
private final Set<DexMethod> publicizedMethods;
- PublicizerLense(AppView appView, Set<DexMethod> publicizedMethods) {
+ private PublicizerLense(AppView appView, Set<DexMethod> publicizedMethods) {
// This lense does not map any DexItem's at all.
// It will just tweak invoke type for publicized methods from invoke-direct to invoke-virtual.
super(
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java b/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
index 7cf8997..9dfddb2 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
@@ -54,15 +54,15 @@
}
public boolean containsAll(AccessFlags other) {
- return containsAll(other.getAsCfAccessFlags());
+ return containsAll(other.getOriginalCfAccessFlags());
}
public boolean containsNone(AccessFlags other) {
- return containsNone(other.getAsCfAccessFlags());
+ return containsNone(other.getOriginalCfAccessFlags());
}
public void setFlags(AccessFlags other) {
- this.flags = other.getAsCfAccessFlags();
+ this.flags = other.getOriginalCfAccessFlags();
}
public void setPublic() {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
index 5424cc2..ae54a80 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
@@ -171,7 +171,6 @@
case ALL:
case ALL_FIELDS:
// Access flags check.
- // TODO(b/117330692): The access flags may have changed as a result of access relaxation.
if (!getAccessFlags().containsAll(field.accessFlags)
|| !getNegatedAccessFlags().containsNone(field.accessFlags)) {
break;
@@ -185,7 +184,6 @@
break;
}
// Access flags check.
- // TODO(b/117330692): The access flags may have changed as a result of access relaxation.
if (!getAccessFlags().containsAll(field.accessFlags)
|| !getNegatedAccessFlags().containsNone(field.accessFlags)) {
break;
@@ -219,7 +217,6 @@
// Fall through for all other methods.
case ALL:
// Access flags check.
- // TODO(b/117330692): The access flags may have changed as a result of access relaxation.
if (!getAccessFlags().containsAll(method.accessFlags)
|| !getNegatedAccessFlags().containsNone(method.accessFlags)) {
break;
@@ -240,7 +237,6 @@
break;
}
// Access flags check.
- // TODO(b/117330692): The access flags may have changed as a result of access relaxation.
if (!getAccessFlags().containsAll(method.accessFlags)
|| !getNegatedAccessFlags().containsNone(method.accessFlags)) {
break;
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 4dfee8e..284b03f 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -1146,11 +1146,11 @@
ParameterAnnotationsList.empty(),
code,
method.hasClassFileVersion() ? method.getClassFileVersion() : -1);
- if (method.getOptimizationInfo().isPublicized()) {
+ if (method.accessFlags.isPromotedToPublic()) {
// The bridge is now the public method serving the role of the original method, and should
// reflect that this method was publicized.
- bridge.getMutableOptimizationInfo().markPublicized();
- method.getMutableOptimizationInfo().unsetPublicized();
+ assert bridge.accessFlags.isPromotedToPublic();
+ method.accessFlags.unsetPromotedToPublic();
}
return bridge;
}
@@ -1345,6 +1345,7 @@
private static void makePrivate(DexEncodedMethod method) {
assert !method.accessFlags.isAbstract();
+ method.accessFlags.unsetPromotedToPublic();
method.accessFlags.unsetPublic();
method.accessFlags.unsetProtected();
method.accessFlags.setPrivate();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
index 592aeae..415376f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
@@ -10,13 +10,10 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8Command;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestRunResult;
-import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.ir.optimize.classinliner.builders.BuildersTestClass;
import com.android.tools.r8.ir.optimize.classinliner.builders.ControlFlow;
@@ -41,7 +38,6 @@
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StringUtils;
@@ -50,10 +46,8 @@
import com.android.tools.r8.utils.codeinspector.FieldAccessInstructionSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.NewInstanceInstructionSubject;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
-import java.nio.file.Path;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;
diff --git a/src/test/java/com/android/tools/r8/naming/MinifierTest.java b/src/test/java/com/android/tools/r8/naming/MinifierTest.java
index 8a5975ea..473ca53 100644
--- a/src/test/java/com/android/tools/r8/naming/MinifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/MinifierTest.java
@@ -98,10 +98,11 @@
// method naming001.A.m should be kept, according to the keep rule.
assertEquals("m", naming.lookupName(m).toSourceString());
- // method naming001.A.privateFunc is transformed to a public method, and then kept.
- DexMethod p = dexItemFactory.createMethod(
- a, dexItemFactory.createProto(dexItemFactory.voidType), "privateFunc");
- assertEquals("privateFunc", naming.lookupName(p).toSourceString());
+ // method naming001.A.privateFunc is not private in the input and should therefore be renamed.
+ DexMethod p =
+ dexItemFactory.createMethod(
+ a, dexItemFactory.createProto(dexItemFactory.voidType), "privateFunc");
+ assertNotEquals("privateFunc", naming.lookupName(p).toSourceString());
}
private static void test001_rule005(DexItemFactory dexItemFactory, NamingLens naming) {
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
index a603f92..d300a4e 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
@@ -5,6 +5,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@@ -102,12 +103,6 @@
assertTrue(methodSubject.getMethod().accessFlags.isPublic());
classSubject = codeInspector.clazz(ClassForSubsequent.class);
- if (shrinker.isR8()) {
- // TODO(b/117330692): ClassForIf#nonPublicMethod becomes public, and -if rule is not applied
- // at the 2nd tree shaking.
- assertThat(classSubject, not(isPresent()));
- return;
- }
assertThat(classSubject, isPresent());
methodSubject = classSubject.method(publicMethod);
assertThat(methodSubject, isPresent());
@@ -141,18 +136,12 @@
assertTrue(methodSubject.getMethod().accessFlags.isPublic());
classSubject = codeInspector.clazz(ClassForSubsequent.class);
- if (shrinker.isR8()) {
- // TODO(b/117330692): ClassForIf#nonPublicMethod becomes public, and -if rule is not applied
- // at the 2nd tree shaking.
- assertThat(classSubject, not(isPresent()));
- return;
- }
assertThat(classSubject, isPresent());
methodSubject = classSubject.method(publicMethod);
assertThat(methodSubject, not(isPresent()));
methodSubject = classSubject.method(nonPublicMethod);
assertThat(methodSubject, isPresent());
- assertFalse(methodSubject.getMethod().accessFlags.isPublic());
+ assertEquals(shrinker.isR8(), methodSubject.getMethod().accessFlags.isPublic());
}
@Test
@@ -217,12 +206,6 @@
assertThat(methodSubject, not(isPresent()));
methodSubject = classSubject.method(nonPublicMethod);
assertThat(methodSubject, isPresent());
- if (shrinker.isR8()) {
- // TODO(b/117330692): if kept in the 1st tree shaking, should not be publicized.
- assertTrue(methodSubject.getMethod().accessFlags.isPublic());
- return;
- }
- assertFalse(methodSubject.getMethod().accessFlags.isPublic());
+ assertEquals(shrinker.isR8(), methodSubject.getMethod().accessFlags.isPublic());
}
-
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/accessrelaxation/IfRuleWithAccessRelaxation.java b/src/test/java/com/android/tools/r8/shaking/ifrule/accessrelaxation/IfRuleWithAccessRelaxation.java
new file mode 100644
index 0000000..cf6d7c0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/accessrelaxation/IfRuleWithAccessRelaxation.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2018, 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.accessrelaxation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+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 IfRuleWithAccessRelaxation extends TestBase {
+
+ private final Backend backend;
+
+ public IfRuleWithAccessRelaxation(Backend backend) {
+ this.backend = backend;
+ }
+
+ @Parameters(name = "Backend: {0}")
+ public static Backend[] data() {
+ return Backend.values();
+ }
+
+ @Test
+ public void r8Test() throws Exception {
+ CodeInspector inspector =
+ testForR8(backend)
+ .addInnerClasses(IfRuleWithAccessRelaxation.class)
+ .addKeepRules(
+ "-keep class " + TestClass.class.getTypeName() + " { int field; void method(); }",
+ "-if class " + TestClass.class.getTypeName() + " { protected int field; }",
+ "-keep class " + Unused1.class.getTypeName(),
+ "-if class " + TestClass.class.getTypeName() + " { protected void method(); }",
+ "-keep class " + Unused2.class.getTypeName(),
+ "-allowaccessmodification")
+ .compile()
+ .inspector();
+
+ assertTrue(inspector.clazz(TestClass.class).isPublic());
+ assertTrue(inspector.clazz(TestClass.class).uniqueMethodWithName("method").isPublic());
+ assertTrue(inspector.clazz(TestClass.class).uniqueFieldWithName("field").isPublic());
+
+ assertThat(inspector.clazz(Unused1.class), isPresent());
+ assertThat(inspector.clazz(Unused2.class), isPresent());
+ }
+
+ protected static class TestClass {
+
+ protected int field = 42;
+
+ protected void method() {}
+ }
+
+ static class Unused1 {}
+
+ static class Unused2 {}
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
index 13e6232..0da4787 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
@@ -37,6 +37,11 @@
}
@Override
+ public FieldSubject uniqueFieldWithName(String name) {
+ return new AbsentFieldSubject();
+ }
+
+ @Override
public boolean isAbstract() {
return false;
}
@@ -47,6 +52,11 @@
}
@Override
+ public boolean isPublic() {
+ return false;
+ }
+
+ @Override
public DexClass getDexClass() {
return null;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
index daf64de..1c9fe9f 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -70,6 +70,8 @@
public abstract FieldSubject field(String type, String name);
+ public abstract FieldSubject uniqueFieldWithName(String name);
+
public FoundClassSubject asFoundClassSubject() {
return null;
}
@@ -78,6 +80,8 @@
public abstract boolean isAnnotation();
+ public abstract boolean isPublic();
+
public String dumpMethods() {
StringBuilder dump = new StringBuilder();
forAllMethods(
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index ed58328..1167d8f 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.naming.ClassNamingForNameMapper;
import com.android.tools.r8.naming.MemberNaming;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
@@ -144,6 +143,18 @@
}
@Override
+ public FieldSubject uniqueFieldWithName(String name) {
+ FieldSubject fieldSubject = null;
+ for (FoundFieldSubject candidate : allFields()) {
+ if (candidate.getOriginalName().equals(name)) {
+ assert fieldSubject == null;
+ fieldSubject = candidate;
+ }
+ }
+ return fieldSubject != null ? fieldSubject : new AbsentFieldSubject();
+ }
+
+ @Override
public FoundClassSubject asFoundClassSubject() {
return this;
}
@@ -154,6 +165,11 @@
}
@Override
+ public boolean isPublic() {
+ return dexClass.accessFlags.isPublic();
+ }
+
+ @Override
public boolean isAnnotation() {
return dexClass.accessFlags.isAnnotation();
}