[KeepAnno] Maintain constraints until rule extraction
This is in preparation for determining the needed keepattribute clauses.
Bug: b/319473429
Change-Id: I5979bea74ceb64a7924528dd307f5f3efc4f69f6
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstraint.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstraint.java
index 7f10457..8cfa99d 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstraint.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstraint.java
@@ -80,6 +80,16 @@
VISIBILITY_RESTRICT,
/**
+ * Indicates that the visibility of the target must remain as declared.
+ *
+ * <p>Note that this constraint does not place any restrictions on any other accesses flags than
+ * visibility. In particular, flags such a static, final and abstract may change.
+ *
+ * <p>This is equivalent to using both {@link #VISIBILITY_RELAX} and {@link #VISIBILITY_RESTRICT}.
+ */
+ VISIBILITY_INVARIANT,
+
+ /**
* Indicates that the class target is being instantiated reflectively.
*
* <p>This usage constraint is only valid on class targets.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/AnnotationVisitorBase.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/AnnotationVisitorBase.java
index 777e99b..87e9e42 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/AnnotationVisitorBase.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/AnnotationVisitorBase.java
@@ -21,6 +21,10 @@
return Type.getType(descriptor).getClassName();
}
+ public ParsingContext getParsingContext() {
+ return parsingContext;
+ }
+
@Override
public void visit(String name, Object value) {
throw parsingContext.error("Unexpected value for property " + name + " with value " + value);
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ConstraintsParser.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ConstraintsParser.java
new file mode 100644
index 0000000..981c480
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ConstraintsParser.java
@@ -0,0 +1,51 @@
+// Copyright (c) 2024, 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.keepanno.asm;
+
+import com.android.tools.r8.keepanno.asm.ConstraintsParser.ConstraintsProperty;
+import com.android.tools.r8.keepanno.ast.AnnotationConstants.Target;
+import com.android.tools.r8.keepanno.ast.KeepConstraints;
+import com.android.tools.r8.keepanno.ast.KeepOptions;
+import com.android.tools.r8.keepanno.ast.ParsingContext;
+import java.util.function.Consumer;
+import org.objectweb.asm.AnnotationVisitor;
+
+public class ConstraintsParser extends PropertyParserBase<KeepConstraints, ConstraintsProperty> {
+
+ public enum ConstraintsProperty {
+ CONSTRAINTS,
+ ALLOW,
+ DISALLOW
+ }
+
+ public ConstraintsParser(ParsingContext parsingContext) {
+ super(parsingContext.group(Target.constraintsGroup));
+ }
+
+ @Override
+ AnnotationVisitor tryPropertyArray(
+ ConstraintsProperty property, String name, Consumer<KeepConstraints> setValue) {
+ switch (property) {
+ case CONSTRAINTS:
+ return new KeepConstraintsVisitor(getParsingContext(), setValue::accept);
+ case ALLOW:
+ return new KeepOptionsVisitor(
+ getParsingContext(),
+ options ->
+ setValue.accept(
+ KeepConstraints.fromLegacyOptions(
+ KeepOptions.allowBuilder().addAll(options).build())));
+ case DISALLOW:
+ return new KeepOptionsVisitor(
+ getParsingContext(),
+ options ->
+ setValue.accept(
+ KeepConstraints.fromLegacyOptions(
+ KeepOptions.disallowBuilder().addAll(options).build())));
+ default:
+ return null;
+ }
+ }
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepConstraintsVisitor.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepConstraintsVisitor.java
index 9012458..b0b58f8 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepConstraintsVisitor.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepConstraintsVisitor.java
@@ -7,19 +7,16 @@
import com.android.tools.r8.keepanno.asm.KeepEdgeReader.Parent;
import com.android.tools.r8.keepanno.ast.AnnotationConstants;
import com.android.tools.r8.keepanno.ast.AnnotationConstants.Constraints;
-import com.android.tools.r8.keepanno.ast.KeepOptions.KeepOption;
+import com.android.tools.r8.keepanno.ast.KeepConstraint;
+import com.android.tools.r8.keepanno.ast.KeepConstraints;
import com.android.tools.r8.keepanno.ast.ParsingContext;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
public class KeepConstraintsVisitor extends AnnotationVisitorBase {
- private final Parent<Collection<KeepOption>> parent;
- private final Set<KeepOption> options = new HashSet<>();
+ private final Parent<KeepConstraints> parent;
+ private final KeepConstraints.Builder builder = KeepConstraints.builder();
- public KeepConstraintsVisitor(
- ParsingContext parsingContext, Parent<Collection<KeepOption>> parent) {
+ public KeepConstraintsVisitor(ParsingContext parsingContext, Parent<KeepConstraints> parent) {
super(parsingContext);
this.parent = parent;
}
@@ -31,38 +28,47 @@
}
switch (value) {
case Constraints.LOOKUP:
- options.add(KeepOption.SHRINKING);
+ builder.add(KeepConstraint.lookup());
break;
case Constraints.NAME:
- options.add(KeepOption.OBFUSCATING);
+ builder.add(KeepConstraint.name());
break;
case Constraints.VISIBILITY_RELAX:
- // The compiler currently satisfies that access is never restricted.
+ builder.add(KeepConstraint.visibilityRelax());
break;
case Constraints.VISIBILITY_RESTRICT:
- // We don't have directional rules so this prohibits any modification.
- options.add(KeepOption.ACCESS_MODIFICATION);
+ builder.add(KeepConstraint.visibilityRestrict());
+ break;
+ case Constraints.VISIBILITY_INVARIANT:
+ builder.add(KeepConstraint.visibilityRelax());
+ builder.add(KeepConstraint.visibilityRestrict());
break;
case Constraints.CLASS_INSTANTIATE:
+ builder.add(KeepConstraint.classInstantiate());
+ break;
case Constraints.METHOD_INVOKE:
+ builder.add(KeepConstraint.methodInvoke());
+ break;
case Constraints.FIELD_GET:
+ builder.add(KeepConstraint.fieldGet());
+ break;
case Constraints.FIELD_SET:
- // These options are the item-specific actual uses of the items.
- // Allocating, invoking and read/writing all imply that the item cannot be "optimized"
- // at compile time. It would be natural to refine the field specific uses but that is
- // not expressible as keep options.
- options.add(KeepOption.OPTIMIZING);
+ builder.add(KeepConstraint.fieldSet());
break;
case Constraints.METHOD_REPLACE:
+ builder.add(KeepConstraint.methodReplace());
+ break;
case Constraints.FIELD_REPLACE:
+ builder.add(KeepConstraint.fieldReplace());
+ break;
case Constraints.NEVER_INLINE:
+ builder.add(KeepConstraint.neverInline());
+ break;
case Constraints.CLASS_OPEN_HIERARCHY:
- options.add(KeepOption.OPTIMIZING);
+ builder.add(KeepConstraint.classOpenHierarchy());
break;
case Constraints.ANNOTATIONS:
- // The annotation constrain only implies that annotations should remain, no restrictions
- // are on the item otherwise.
- options.add(KeepOption.ANNOTATION_REMOVAL);
+ builder.add(KeepConstraint.annotationsAll());
break;
default:
super.visitEnum(ignore, descriptor, value);
@@ -71,7 +77,7 @@
@Override
public void visitEnd() {
- parent.accept(options);
+ parent.accept(builder.build());
super.visitEnd();
}
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
index b8b81ae..c0d6087 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
@@ -4,8 +4,8 @@
package com.android.tools.r8.keepanno.asm;
import com.android.tools.r8.keepanno.asm.ClassNameParser.ClassNameProperty;
+import com.android.tools.r8.keepanno.asm.ConstraintsParser.ConstraintsProperty;
import com.android.tools.r8.keepanno.asm.InstanceOfParser.InstanceOfProperties;
-import com.android.tools.r8.keepanno.asm.OptionsParser.OptionsProperty;
import com.android.tools.r8.keepanno.asm.StringPatternParser.StringProperty;
import com.android.tools.r8.keepanno.asm.TypeParser.TypeProperty;
import com.android.tools.r8.keepanno.ast.AccessVisibility;
@@ -30,6 +30,7 @@
import com.android.tools.r8.keepanno.ast.KeepClassItemReference;
import com.android.tools.r8.keepanno.ast.KeepCondition;
import com.android.tools.r8.keepanno.ast.KeepConsequences;
+import com.android.tools.r8.keepanno.ast.KeepConstraints;
import com.android.tools.r8.keepanno.ast.KeepDeclaration;
import com.android.tools.r8.keepanno.ast.KeepEdge;
import com.android.tools.r8.keepanno.ast.KeepEdgeMetaInfo;
@@ -49,8 +50,6 @@
import com.android.tools.r8.keepanno.ast.KeepMethodParametersPattern;
import com.android.tools.r8.keepanno.ast.KeepMethodPattern;
import com.android.tools.r8.keepanno.ast.KeepMethodReturnTypePattern;
-import com.android.tools.r8.keepanno.ast.KeepOptions;
-import com.android.tools.r8.keepanno.ast.KeepOptions.KeepOption;
import com.android.tools.r8.keepanno.ast.KeepPreconditions;
import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern;
import com.android.tools.r8.keepanno.ast.KeepStringPattern;
@@ -96,22 +95,8 @@
KeepQualifiedClassNamePattern.exact(className));
}
- private static KeepOptions getKeepOptionsOrDefault(KeepOptions options) {
- // TODO(b/248408342): These should be constraints computed/filtered based on the item type but
- // currently the constraints default to the same set of options.
- if (options != null) {
- return options;
- }
- return KeepOptions.disallowBuilder()
- // LOOKUP (same for any type of item).
- .add(KeepOption.SHRINKING)
- // NAME (same for any type of item).
- .add(KeepOption.OBFUSCATING)
- // CLASS_INSTANTIATE / METHOD_INVOKE / FIELD_GET & FIELD_SET:
- .add(KeepOption.OPTIMIZING)
- .add(KeepOption.ACCESS_MODIFICATION)
- // ACCESS_ALLOW - currently no options needed.
- .build();
+ private static KeepConstraints getClassConstraintsOrDefault(KeepConstraints constraints) {
+ return constraints != null ? constraints : KeepConstraints.defaultConstraints();
}
/** Internal copy of the user-facing KeepItemKind */
@@ -746,7 +731,7 @@
private final KeepConsequences.Builder consequences = KeepConsequences.builder();
private final KeepEdgeMetaInfo.Builder metaInfoBuilder = KeepEdgeMetaInfo.builder();
private final UserBindingsHelper bindingsHelper = new UserBindingsHelper();
- private final OptionsParser optionsParser;
+ private final ConstraintsParser constraintsParser;
UsedByReflectionClassVisitor(
AnnotationParsingContext parsingContext,
@@ -760,10 +745,10 @@
addContext.accept(metaInfoBuilder);
// The class context/holder is the annotated class.
visit(Item.className, className);
- optionsParser = new OptionsParser(parsingContext);
- optionsParser.setProperty(Target.constraints, OptionsProperty.CONSTRAINTS);
- optionsParser.setProperty(Target.allow, OptionsProperty.ALLOW);
- optionsParser.setProperty(Target.disallow, OptionsProperty.DISALLOW);
+ constraintsParser = new ConstraintsParser(parsingContext);
+ constraintsParser.setProperty(Target.constraints, ConstraintsProperty.CONSTRAINTS);
+ constraintsParser.setProperty(Target.allow, ConstraintsProperty.ALLOW);
+ constraintsParser.setProperty(Target.disallow, ConstraintsProperty.DISALLOW);
}
@Override
@@ -794,7 +779,7 @@
},
bindingsHelper);
}
- AnnotationVisitor visitor = optionsParser.tryParseArray(name, unused -> {});
+ AnnotationVisitor visitor = constraintsParser.tryParseArray(name, unused -> {});
if (visitor != null) {
return visitor;
}
@@ -830,7 +815,8 @@
consequences.addTarget(
KeepTarget.builder()
.setItemPattern(itemPattern)
- .setOptions(getKeepOptionsOrDefault(optionsParser.getValue()))
+ .setConstraints(
+ constraintsParser.getValueOrDefault(KeepConstraints.defaultConstraints()))
.build());
}
parent.accept(
@@ -857,7 +843,7 @@
private final UserBindingsHelper bindingsHelper = new UserBindingsHelper();
private final KeepConsequences.Builder consequences = KeepConsequences.builder();
private ItemKind kind = KeepEdgeReader.ItemKind.ONLY_MEMBERS;
- private final OptionsParser optionsParser;
+ private final ConstraintsParser constraintsParser;
UsedByReflectionMemberVisitor(
AnnotationParsingContext parsingContext,
@@ -869,10 +855,10 @@
this.parent = parent;
this.context = context;
addContext.accept(metaInfoBuilder);
- optionsParser = new OptionsParser(parsingContext);
- optionsParser.setProperty(Target.constraints, OptionsProperty.CONSTRAINTS);
- optionsParser.setProperty(Target.allow, OptionsProperty.ALLOW);
- optionsParser.setProperty(Target.disallow, OptionsProperty.DISALLOW);
+ constraintsParser = new ConstraintsParser(parsingContext);
+ constraintsParser.setProperty(Target.constraints, ConstraintsProperty.CONSTRAINTS);
+ constraintsParser.setProperty(Target.allow, ConstraintsProperty.ALLOW);
+ constraintsParser.setProperty(Target.disallow, ConstraintsProperty.DISALLOW);
}
@Override
@@ -911,7 +897,7 @@
},
bindingsHelper);
}
- AnnotationVisitor visitor = optionsParser.tryParseArray(name, unused -> {});
+ AnnotationVisitor visitor = constraintsParser.tryParseArray(name, unused -> {});
if (visitor != null) {
return visitor;
}
@@ -932,7 +918,8 @@
validateConsistentKind(memberContext.getMemberPattern());
consequences.addTarget(
KeepTarget.builder()
- .setOptions(getKeepOptionsOrDefault(optionsParser.getValue()))
+ .setConstraints(
+ constraintsParser.getValueOrDefault(KeepConstraints.defaultConstraints()))
.setItemPattern(context)
.build());
parent.accept(
@@ -1803,7 +1790,7 @@
private final Parent<KeepTarget> parent;
private final UserBindingsHelper bindingsHelper;
- private final OptionsParser optionsParser;
+ private final ConstraintsParser optionsParser;
private final KeepTarget.Builder builder = KeepTarget.builder();
static KeepTargetVisitor create(
@@ -1820,10 +1807,10 @@
super(parsingContext);
this.parent = parent;
this.bindingsHelper = bindingsHelper;
- optionsParser = new OptionsParser(parsingContext);
- optionsParser.setProperty(Target.constraints, OptionsProperty.CONSTRAINTS);
- optionsParser.setProperty(Target.allow, OptionsProperty.ALLOW);
- optionsParser.setProperty(Target.disallow, OptionsProperty.DISALLOW);
+ optionsParser = new ConstraintsParser(parsingContext);
+ optionsParser.setProperty(Target.constraints, ConstraintsProperty.CONSTRAINTS);
+ optionsParser.setProperty(Target.allow, ConstraintsProperty.ALLOW);
+ optionsParser.setProperty(Target.disallow, ConstraintsProperty.DISALLOW);
}
@Override
@@ -1843,7 +1830,7 @@
@Override
public void visitEnd() {
super.visitEnd();
- builder.setOptions(getKeepOptionsOrDefault(optionsParser.getValue()));
+ builder.setConstraints(optionsParser.getValueOrDefault(KeepConstraints.defaultConstraints()));
for (KeepItemReference item : getItemsWithBinding()) {
parent.accept(builder.setItemReference(item).build());
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
index 87e2624..565067d 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
@@ -124,10 +124,6 @@
target -> {
AnnotationVisitor targetVisitor =
arrayVisitor.visitAnnotation(ignoredArrayValueName, Target.DESCRIPTOR);
- // No options imply keep all.
- if (!target.getOptions().isKeepAll()) {
- throw new Unimplemented();
- }
if (target.getItem().isBindingReference()) {
throw new Unimplemented();
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/OptionsParser.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/OptionsParser.java
deleted file mode 100644
index dd2beda..0000000
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/OptionsParser.java
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (c) 2024, 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.keepanno.asm;
-
-import com.android.tools.r8.keepanno.asm.OptionsParser.OptionsProperty;
-import com.android.tools.r8.keepanno.ast.AnnotationConstants.Target;
-import com.android.tools.r8.keepanno.ast.KeepOptions;
-import com.android.tools.r8.keepanno.ast.ParsingContext;
-import java.util.function.Consumer;
-import org.objectweb.asm.AnnotationVisitor;
-
-public class OptionsParser extends PropertyParserBase<KeepOptions, OptionsProperty> {
-
- public enum OptionsProperty {
- CONSTRAINTS,
- ALLOW,
- DISALLOW
- }
-
- public OptionsParser(ParsingContext parsingContext) {
- super(parsingContext.group(Target.constraintsGroup));
- }
-
- @Override
- AnnotationVisitor tryPropertyArray(
- OptionsProperty property, String name, Consumer<KeepOptions> setValue) {
- switch (property) {
- case CONSTRAINTS:
- return new KeepConstraintsVisitor(
- getParsingContext(),
- options -> setValue.accept(KeepOptions.disallowBuilder().addAll(options).build()));
- case ALLOW:
- return new KeepOptionsVisitor(
- getParsingContext(),
- options -> setValue.accept(KeepOptions.allowBuilder().addAll(options).build()));
- case DISALLOW:
- return new KeepOptionsVisitor(
- getParsingContext(),
- options -> setValue.accept(KeepOptions.disallowBuilder().addAll(options).build()));
- default:
- return null;
- }
- }
-}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java
index 2ac416c..85082de 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java
@@ -163,6 +163,7 @@
public static final String NAME = "NAME";
public static final String VISIBILITY_RELAX = "VISIBILITY_RELAX";
public static final String VISIBILITY_RESTRICT = "VISIBILITY_RESTRICT";
+ public static final String VISIBILITY_INVARIANT = "VISIBILITY_INVARIANT";
public static final String CLASS_INSTANTIATE = "CLASS_INSTANTIATE";
public static final String METHOD_INVOKE = "METHOD_INVOKE";
public static final String FIELD_GET = "FIELD_GET";
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepConstraint.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepConstraint.java
new file mode 100644
index 0000000..6283ec6
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepConstraint.java
@@ -0,0 +1,327 @@
+// Copyright (c) 2024, 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.keepanno.ast;
+
+import com.android.tools.r8.keepanno.ast.KeepOptions.KeepOption;
+
+public abstract class KeepConstraint {
+
+ @Override
+ public String toString() {
+ String typeName = getClass().getTypeName();
+ return typeName.substring(typeName.lastIndexOf('$') + 1);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return this == obj;
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(this);
+ }
+
+ public abstract void convertToDisallowKeepOptions(KeepOptions.Builder builder);
+
+ public final boolean validForClass() {
+ return !isMethodOnly() && !isFieldOnly();
+ }
+
+ public final boolean validForMethod() {
+ return !isClassOnly() && !isFieldOnly();
+ }
+
+ public final boolean validForField() {
+ return !isClassOnly() && !isMethodOnly();
+ }
+
+ boolean isClassOnly() {
+ return false;
+ }
+
+ boolean isMethodOnly() {
+ return false;
+ }
+
+ boolean isFieldOnly() {
+ return false;
+ }
+
+ public static Lookup lookup() {
+ return Lookup.INSTANCE;
+ }
+
+ public static final class Lookup extends KeepConstraint {
+
+ private static final Lookup INSTANCE = new Lookup();
+
+ private Lookup() {}
+
+ @Override
+ public void convertToDisallowKeepOptions(KeepOptions.Builder builder) {
+ builder.add(KeepOption.SHRINKING);
+ }
+ }
+
+ public static Name name() {
+ return Name.INSTANCE;
+ }
+
+ public static final class Name extends KeepConstraint {
+
+ private static final Name INSTANCE = new Name();
+
+ private Name() {}
+
+ @Override
+ public void convertToDisallowKeepOptions(KeepOptions.Builder builder) {
+ builder.add(KeepOption.OBFUSCATING);
+ }
+ }
+
+ public static VisibilityRelax visibilityRelax() {
+ return VisibilityRelax.INSTANCE;
+ }
+
+ public static final class VisibilityRelax extends KeepConstraint {
+
+ private static final VisibilityRelax INSTANCE = new VisibilityRelax();
+
+ private VisibilityRelax() {}
+
+ @Override
+ public void convertToDisallowKeepOptions(KeepOptions.Builder builder) {
+ // The compiler currently satisfies that access is never restricted.
+ }
+ }
+
+ public static VisibilityRestrict visibilityRestrict() {
+ return VisibilityRestrict.INSTANCE;
+ }
+
+ public static final class VisibilityRestrict extends KeepConstraint {
+
+ private static final VisibilityRestrict INSTANCE = new VisibilityRestrict();
+
+ private VisibilityRestrict() {}
+
+ @Override
+ public void convertToDisallowKeepOptions(KeepOptions.Builder builder) {
+ // We don't have directional rules so this prohibits any modification.
+ builder.add(KeepOption.ACCESS_MODIFICATION);
+ }
+ }
+
+ public static NeverInline neverInline() {
+ return NeverInline.INSTANCE;
+ }
+
+ public static final class NeverInline extends KeepConstraint {
+
+ private static final NeverInline INSTANCE = new NeverInline();
+
+ private NeverInline() {}
+
+ @Override
+ public void convertToDisallowKeepOptions(KeepOptions.Builder builder) {
+ builder.add(KeepOption.OPTIMIZING);
+ }
+ }
+
+ public static ClassInstantiate classInstantiate() {
+ return ClassInstantiate.INSTANCE;
+ }
+
+ public static final class ClassInstantiate extends KeepConstraint {
+
+ private static final ClassInstantiate INSTANCE = new ClassInstantiate();
+
+ private ClassInstantiate() {}
+
+ @Override
+ boolean isClassOnly() {
+ return true;
+ }
+
+ @Override
+ public void convertToDisallowKeepOptions(KeepOptions.Builder builder) {
+ builder.add(KeepOption.OPTIMIZING);
+ }
+ }
+
+ public static ClassOpenHierarchy classOpenHierarchy() {
+ return ClassOpenHierarchy.INSTANCE;
+ }
+
+ public static final class ClassOpenHierarchy extends KeepConstraint {
+
+ private static final ClassOpenHierarchy INSTANCE = new ClassOpenHierarchy();
+
+ private ClassOpenHierarchy() {}
+
+ @Override
+ boolean isClassOnly() {
+ return true;
+ }
+
+ @Override
+ public void convertToDisallowKeepOptions(KeepOptions.Builder builder) {
+ builder.add(KeepOption.OPTIMIZING);
+ }
+ }
+
+ public static MethodInvoke methodInvoke() {
+ return MethodInvoke.INSTANCE;
+ }
+
+ public static final class MethodInvoke extends KeepConstraint {
+
+ private static final MethodInvoke INSTANCE = new MethodInvoke();
+
+ private MethodInvoke() {}
+
+ @Override
+ boolean isMethodOnly() {
+ return true;
+ }
+
+ @Override
+ public void convertToDisallowKeepOptions(KeepOptions.Builder builder) {
+ builder.add(KeepOption.OPTIMIZING);
+ }
+ }
+
+ public static MethodReplace methodReplace() {
+ return MethodReplace.INSTANCE;
+ }
+
+ public static final class MethodReplace extends KeepConstraint {
+
+ private static final MethodReplace INSTANCE = new MethodReplace();
+
+ private MethodReplace() {}
+
+ @Override
+ boolean isMethodOnly() {
+ return true;
+ }
+
+ @Override
+ public void convertToDisallowKeepOptions(KeepOptions.Builder builder) {
+ builder.add(KeepOption.OPTIMIZING);
+ }
+ }
+
+ public static FieldGet fieldGet() {
+ return FieldGet.INSTANCE;
+ }
+
+ public static final class FieldGet extends KeepConstraint {
+
+ private static final FieldGet INSTANCE = new FieldGet();
+
+ private FieldGet() {}
+
+ @Override
+ boolean isFieldOnly() {
+ return true;
+ }
+
+ @Override
+ public void convertToDisallowKeepOptions(KeepOptions.Builder builder) {
+ builder.add(KeepOption.OPTIMIZING);
+ }
+ }
+
+ public static FieldSet fieldSet() {
+ return FieldSet.INSTANCE;
+ }
+
+ public static final class FieldSet extends KeepConstraint {
+
+ private static final FieldSet INSTANCE = new FieldSet();
+
+ private FieldSet() {}
+
+ @Override
+ boolean isFieldOnly() {
+ return true;
+ }
+
+ @Override
+ public void convertToDisallowKeepOptions(KeepOptions.Builder builder) {
+ builder.add(KeepOption.OPTIMIZING);
+ }
+ }
+
+ public static FieldReplace fieldReplace() {
+ return FieldReplace.INSTANCE;
+ }
+
+ public static final class FieldReplace extends KeepConstraint {
+
+ private static final FieldReplace INSTANCE = new FieldReplace();
+
+ private FieldReplace() {}
+
+ @Override
+ boolean isFieldOnly() {
+ return true;
+ }
+
+ @Override
+ public void convertToDisallowKeepOptions(KeepOptions.Builder builder) {
+ builder.add(KeepOption.OPTIMIZING);
+ }
+ }
+
+ public static Annotation annotationsAll() {
+ return Annotation.ALL_INSTANCE;
+ }
+
+ public static Annotation annotation(KeepQualifiedClassNamePattern pattern) {
+ if (pattern.isAny()) {
+ return annotationsAll();
+ }
+ return new Annotation(pattern);
+ }
+
+ public static final class Annotation extends KeepConstraint {
+
+ private static final Annotation ALL_INSTANCE =
+ new Annotation(KeepQualifiedClassNamePattern.any());
+
+ private final KeepQualifiedClassNamePattern classNamePattern;
+
+ private Annotation(KeepQualifiedClassNamePattern classNamePattern) {
+ this.classNamePattern = classNamePattern;
+ }
+
+ @Override
+ public void convertToDisallowKeepOptions(KeepOptions.Builder builder) {
+ // The annotation constraint only implies that annotations should remain, no restrictions
+ // are on the item otherwise. Also, we can't restrict the rule to just the annotations being
+ // constrained in the legacy rules.
+ builder.add(KeepOption.ANNOTATION_REMOVAL);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Annotation)) {
+ return false;
+ }
+ Annotation that = (Annotation) o;
+ return classNamePattern.equals(that);
+ }
+
+ @Override
+ public int hashCode() {
+ return classNamePattern.hashCode();
+ }
+ }
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepConstraints.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepConstraints.java
new file mode 100644
index 0000000..669a0b1
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepConstraints.java
@@ -0,0 +1,103 @@
+// Copyright (c) 2024, 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.keepanno.ast;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public abstract class KeepConstraints {
+
+ public static KeepConstraints fromLegacyOptions(KeepOptions options) {
+ return new LegacyOptions(options);
+ }
+
+ public static KeepConstraints defaultConstraints() {
+ return Defaults.INSTANCE;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+
+ private final Set<KeepConstraint> constraints = new HashSet<>();
+
+ private Builder() {}
+
+ public Builder add(KeepConstraint constraint) {
+ constraints.add(constraint);
+ return this;
+ }
+
+ public KeepConstraints build() {
+ return new Constraints(constraints);
+ }
+ }
+
+ private static class LegacyOptions extends KeepConstraints {
+
+ private final KeepOptions options;
+
+ private LegacyOptions(KeepOptions options) {
+ this.options = options;
+ }
+
+ @Override
+ public String toString() {
+ return options.toString();
+ }
+
+ @Override
+ public KeepOptions convertToKeepOptions(KeepOptions defaultOptions) {
+ return options;
+ }
+ }
+
+ private static class Defaults extends KeepConstraints {
+
+ private static final Defaults INSTANCE = new Defaults();
+
+ @Override
+ public KeepOptions convertToKeepOptions(KeepOptions defaultOptions) {
+ return defaultOptions;
+ }
+
+ @Override
+ public String toString() {
+ return "KeepConstraints.Defaults{}";
+ }
+ }
+
+ private static class Constraints extends KeepConstraints {
+
+ private final Set<KeepConstraint> constraints;
+
+ public Constraints(Set<KeepConstraint> constraints) {
+ this.constraints = ImmutableSet.copyOf(constraints);
+ }
+
+ @Override
+ public KeepOptions convertToKeepOptions(KeepOptions defaultOptions) {
+ KeepOptions.Builder builder = KeepOptions.disallowBuilder();
+ for (KeepConstraint constraint : constraints) {
+ constraint.convertToDisallowKeepOptions(builder);
+ }
+ return builder.build();
+ }
+
+ @Override
+ public String toString() {
+ return "KeepConstraints{"
+ + constraints.stream().map(Objects::toString).collect(Collectors.joining(", "))
+ + '}';
+ }
+ }
+
+ public abstract KeepOptions convertToKeepOptions(KeepOptions defaultOptions);
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
index 713686a..b894eae 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
@@ -35,6 +35,24 @@
*
* CONSEQUENCES ::= TARGET+
* TARGET ::= OPTIONS ITEM_REFERENCE
+ *
+ * CONSTRAINTS ::= CONSTRAINT+ | OPTIONS (legacy)
+ * CONSTRAINT
+ * ::= lookup
+ * | name
+ * | visibility-relax
+ * | visibility-restrict
+ * | annotations(QUALIFIED_CLASS_NAME_PATTERN*)
+ * | never-inline
+ * | class-instantiate
+ * | class-open-hierarchy
+ * | method-invoke
+ * | method-replace
+ * | field-get
+ * | field-set
+ * | field-replace
+ *
+ * (legacy options - deprecated)
* OPTIONS ::= keep-all | OPTION+
* OPTION ::= shrinking | optimizing | obfuscating | access-modification | annotation-removal
*
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTarget.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTarget.java
index 1d8b32b..e6bcf49 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTarget.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTarget.java
@@ -10,7 +10,7 @@
public static class Builder {
private KeepItemReference item;
- private KeepOptions options = KeepOptions.keepAll();
+ private KeepConstraints constraints = KeepConstraints.defaultConstraints();
private Builder() {}
@@ -24,7 +24,11 @@
}
public Builder setOptions(KeepOptions options) {
- this.options = options;
+ return setConstraints(KeepConstraints.fromLegacyOptions(options));
+ }
+
+ public Builder setConstraints(KeepConstraints constraints) {
+ this.constraints = constraints;
return this;
}
@@ -32,18 +36,18 @@
if (item == null) {
throw new KeepEdgeException("Target must define an item pattern");
}
- return new KeepTarget(item, options);
+ return new KeepTarget(item, constraints);
}
}
private final KeepItemReference item;
- private final KeepOptions options;
+ private final KeepConstraints constraints;
- private KeepTarget(KeepItemReference item, KeepOptions options) {
+ private KeepTarget(KeepItemReference item, KeepConstraints constraints) {
assert item != null;
- assert options != null;
+ assert constraints != null;
this.item = item;
- this.options = options;
+ this.constraints = constraints;
}
public static Builder builder() {
@@ -54,30 +58,29 @@
return item;
}
- public KeepOptions getOptions() {
- return options;
+ public KeepConstraints getConstraints() {
+ return constraints;
}
@Override
- @SuppressWarnings("EqualsGetClass")
public boolean equals(Object o) {
if (this == o) {
return true;
}
- if (o == null || getClass() != o.getClass()) {
+ if (!(o instanceof KeepTarget)) {
return false;
}
KeepTarget that = (KeepTarget) o;
- return item.equals(that.item) && options.equals(that.options);
+ return item.equals(that.item) && constraints.equals(that.constraints);
}
@Override
public int hashCode() {
- return Objects.hash(item, options);
+ return Objects.hash(item, constraints);
}
@Override
public String toString() {
- return "KeepTarget{" + "item=" + item + ", options=" + options + '}';
+ return "KeepTarget{" + "item=" + item + ", constraints=" + constraints + '}';
}
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepEdgeNormalizer.java b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepEdgeNormalizer.java
index baf9f6c..9806026 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepEdgeNormalizer.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepEdgeNormalizer.java
@@ -65,7 +65,7 @@
target -> {
consequencesBuilder.addTarget(
KeepTarget.builder()
- .setOptions(target.getOptions())
+ .setConstraints(target.getConstraints())
.setItemReference(normalizeItemReference(target.getItem()))
.build());
});
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
index 41f593e..a84754d 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.keepanno.ast.KeepCheck.KeepCheckKind;
import com.android.tools.r8.keepanno.ast.KeepClassItemPattern;
import com.android.tools.r8.keepanno.ast.KeepCondition;
+import com.android.tools.r8.keepanno.ast.KeepConstraints;
import com.android.tools.r8.keepanno.ast.KeepDeclaration;
import com.android.tools.r8.keepanno.ast.KeepEdge;
import com.android.tools.r8.keepanno.ast.KeepEdgeException;
@@ -208,6 +209,11 @@
}
}
+ private static KeepOptions defaultOptions =
+ KeepOptions.disallowBuilder()
+ .addAll(KeepOption.SHRINKING, KeepOption.OBFUSCATING, KeepOption.OPTIMIZING)
+ .build();
+
private static class BindingUsers {
final Holder holder;
@@ -229,8 +235,10 @@
public void addTarget(KeepTarget target) {
assert target.getItem().isBindingReference();
+ KeepConstraints constraints = target.getConstraints();
+ KeepOptions options = constraints.convertToKeepOptions(defaultOptions);
targetRefs
- .computeIfAbsent(target.getOptions(), k -> new HashSet<>())
+ .computeIfAbsent(options, k -> new HashSet<>())
.add(target.getItem().asBindingReference().getName());
}
}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepAccessVisibilityFlagsTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepAccessVisibilityFlagsTest.java
index 4505562..2454356 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepAccessVisibilityFlagsTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepAccessVisibilityFlagsTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.keepanno.annotations.FieldAccessFlags;
+import com.android.tools.r8.keepanno.annotations.KeepConstraint;
import com.android.tools.r8.keepanno.annotations.KeepItemKind;
import com.android.tools.r8.keepanno.annotations.KeepTarget;
import com.android.tools.r8.keepanno.annotations.MemberAccessFlags;
@@ -186,14 +187,29 @@
@KeepTarget(
kind = KeepItemKind.CLASS_AND_MEMBERS,
classConstant = FieldRuleTarget.class,
+ constraints = {
+ KeepConstraint.LOOKUP,
+ KeepConstraint.NAME,
+ KeepConstraint.VISIBILITY_INVARIANT
+ },
fieldAccess = {FieldAccessFlags.NON_PRIVATE}),
@KeepTarget(
kind = KeepItemKind.CLASS_AND_MEMBERS,
classConstant = MethodRuleTarget.class,
+ constraints = {
+ KeepConstraint.LOOKUP,
+ KeepConstraint.NAME,
+ KeepConstraint.VISIBILITY_INVARIANT
+ },
methodAccess = {MethodAccessFlags.NON_PACKAGE_PRIVATE}),
@KeepTarget(
kind = KeepItemKind.CLASS_AND_MEMBERS,
classConstant = MemberRuleTarget.class,
+ constraints = {
+ KeepConstraint.LOOKUP,
+ KeepConstraint.NAME,
+ KeepConstraint.VISIBILITY_INVARIANT
+ },
memberAccess = {MemberAccessFlags.PACKAGE_PRIVATE, MemberAccessFlags.PRIVATE}),
})
void foo() {
diff --git a/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java b/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
index dae2c6b..c504c4a 100644
--- a/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
@@ -53,7 +53,8 @@
.build();
assertEquals(
StringUtils.unixLines(
- "-keep class ** { void finalize(); }", "-keepclassmembers class ** { *; }"),
+ "-keep,allowaccessmodification class ** { void finalize(); }",
+ "-keepclassmembers,allowaccessmodification class ** { *; }"),
extract(edge));
}
@@ -121,7 +122,9 @@
KeepConsequences consequences = KeepConsequences.builder().addTarget(target).build();
KeepEdge edge = KeepEdge.builder().setConsequences(consequences).build();
assertEquals(
- StringUtils.unixLines("-keep class " + CLASS + " { void finalize(); }"), extract(edge));
+ StringUtils.unixLines(
+ "-keep,allowaccessmodification class " + CLASS + " { void finalize(); }"),
+ extract(edge));
}
@Test
@@ -142,7 +145,8 @@
.build())
.build();
assertEquals(
- StringUtils.unixLines("-keepclassmembers class " + CLASS + " { void <init>(); }"),
+ StringUtils.unixLines(
+ "-keepclassmembers,allowaccessmodification class " + CLASS + " { void <init>(); }"),
extract(edge));
}
@@ -158,7 +162,11 @@
.build();
assertEquals(
StringUtils.unixLines(
- "-if class " + CLASS + " -keep class " + CLASS + " { void finalize(); }"),
+ "-if class "
+ + CLASS
+ + " -keep,allowaccessmodification class "
+ + CLASS
+ + " { void finalize(); }"),
extract(edge));
}
@@ -182,8 +190,12 @@
.build();
assertEquals(
StringUtils.unixLines(
- "-keepclassmembers class " + CLASS + " { void <init>(); }",
- "-if class " + CLASS + " -keep class " + CLASS + " { void finalize(); }"),
+ "-keepclassmembers,allowaccessmodification class " + CLASS + " { void <init>(); }",
+ "-if class "
+ + CLASS
+ + " -keep,allowaccessmodification class "
+ + CLASS
+ + " { void finalize(); }"),
extract(edge));
}
@@ -216,7 +228,7 @@
StringUtils.unixLines(
"-if class "
+ CLASS
- + " -keepclasseswithmembers class "
+ + " -keepclasseswithmembers,allowaccessmodification class "
+ CLASS
+ " { void <init>(); }"),
extract(edge));