Merge commit 'bca9efd8de9ca3af84deef1855671a67ee1afb43' into dev-release
Change-Id: Icebe5a3a03186699f55cdbb6c40e8f4ee1776702
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java
index 3f1152e..b98f4ab 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java
@@ -306,11 +306,27 @@
* <p>If none, and other properties define this item as a method, the default matches any method
* name.
*
+ * <p>Mutually exclusive with the property `methodNamePattern` also defining method-name.
+ *
* @return The exact method name of the method.
*/
String methodName() default "";
/**
+ * Define the method-name pattern by a string pattern.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any method
+ * name.
+ *
+ * <p>Mutually exclusive with the property `methodName` also defining method-name.
+ *
+ * @return The string pattern of the method name.
+ */
+ StringPattern methodNamePattern() default @StringPattern(exact = "");
+
+ /**
* Define the method return-type pattern by a fully qualified type or 'void'.
*
* <p>Mutually exclusive with all field properties.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java
index 74b2add..bab5ca7 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java
@@ -254,6 +254,8 @@
* <p>Mutually exclusive with all field and method properties as use restricts the match to both
* types of members.
*
+ * <p>Mutually exclusive with the property `memberFromBinding` also defining member-access.
+ *
* @return The member access-flag constraints that must be met.
*/
MemberAccessFlags[] memberAccess() default {};
@@ -266,6 +268,8 @@
* <p>If none, and other properties define this item as a method, the default matches any
* method-access flags.
*
+ * <p>Mutually exclusive with the property `memberFromBinding` also defining method-access.
+ *
* @return The method access-flag constraints that must be met.
*/
MethodAccessFlags[] methodAccess() default {};
@@ -278,11 +282,37 @@
* <p>If none, and other properties define this item as a method, the default matches any method
* name.
*
+ * <p>Mutually exclusive with the following other properties defining method-name:
+ *
+ * <ul>
+ * <li>methodNamePattern
+ * <li>memberFromBinding
+ * </ul>
+ *
* @return The exact method name of the method.
*/
String methodName() default "";
/**
+ * Define the method-name pattern by a string pattern.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any method
+ * name.
+ *
+ * <p>Mutually exclusive with the following other properties defining method-name:
+ *
+ * <ul>
+ * <li>methodName
+ * <li>memberFromBinding
+ * </ul>
+ *
+ * @return The string pattern of the method name.
+ */
+ StringPattern methodNamePattern() default @StringPattern(exact = "");
+
+ /**
* Define the method return-type pattern by a fully qualified type or 'void'.
*
* <p>Mutually exclusive with all field properties.
@@ -295,6 +325,7 @@
* <ul>
* <li>methodReturnTypeConstant
* <li>methodReturnTypePattern
+ * <li>memberFromBinding
* </ul>
*
* @return The qualified type name of the method return type.
@@ -314,6 +345,7 @@
* <ul>
* <li>methodReturnType
* <li>methodReturnTypePattern
+ * <li>memberFromBinding
* </ul>
*
* @return A class constant denoting the type of the method return type.
@@ -333,6 +365,7 @@
* <ul>
* <li>methodReturnType
* <li>methodReturnTypeConstant
+ * <li>memberFromBinding
* </ul>
*
* @return The pattern of the method return type.
@@ -347,7 +380,12 @@
* <p>If none, and other properties define this item as a method, the default matches any
* parameters.
*
- * <p>Mutually exclusive with the property `methodParameterTypePatterns` also defining parameters.
+ * <p>Mutually exclusive with the following other properties defining parameters:
+ *
+ * <ul>
+ * <li>methodParameterTypePatterns
+ * <li>memberFromBinding
+ * </ul>
*
* @return The list of qualified type names of the method parameters.
*/
@@ -361,7 +399,12 @@
* <p>If none, and other properties define this item as a method, the default matches any
* parameters.
*
- * <p>Mutually exclusive with the property `methodParameters` also defining parameters.
+ * <p>Mutually exclusive with the following other properties defining parameters:
+ *
+ * <ul>
+ * <li>methodParameters
+ * <li>memberFromBinding
+ * </ul>
*
* @return The list of type patterns for the method parameters.
*/
@@ -375,6 +418,8 @@
* <p>If none, and other properties define this item as a field, the default matches any
* field-access flags.
*
+ * <p>Mutually exclusive with the property `memberFromBinding` also defining field-access.
+ *
* @return The field access-flag constraints that must be met.
*/
FieldAccessFlags[] fieldAccess() default {};
@@ -387,6 +432,8 @@
* <p>If none, and other properties define this item as a field, the default matches any field
* name.
*
+ * <p>Mutually exclusive with the property `memberFromBinding` also defining field-name.
+ *
* @return The exact field name of the field.
*/
String fieldName() default "";
@@ -403,6 +450,7 @@
* <ul>
* <li>fieldTypeConstant
* <li>fieldTypePattern
+ * <li>memberFromBinding
* </ul>
*
* @return The qualified type name for the field type.
@@ -421,6 +469,7 @@
* <ul>
* <li>fieldType
* <li>fieldTypePattern
+ * <li>memberFromBinding
* </ul>
*
* @return The class constant for the field type.
@@ -439,6 +488,7 @@
* <ul>
* <li>fieldType
* <li>fieldTypeConstant
+ * <li>memberFromBinding
* </ul>
*
* @return The type pattern for the field type.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepForApi.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepForApi.java
index 5ece578..9d097f0 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepForApi.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepForApi.java
@@ -85,11 +85,27 @@
* <p>If none, and other properties define this item as a method, the default matches any method
* name.
*
+ * <p>Mutually exclusive with the property `methodNamePattern` also defining method-name.
+ *
* @return The exact method name of the method.
*/
String methodName() default "";
/**
+ * Define the method-name pattern by a string pattern.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any method
+ * name.
+ *
+ * <p>Mutually exclusive with the property `methodName` also defining method-name.
+ *
+ * @return The string pattern of the method name.
+ */
+ StringPattern methodNamePattern() default @StringPattern(exact = "");
+
+ /**
* Define the method return-type pattern by a fully qualified type or 'void'.
*
* <p>Mutually exclusive with all field properties.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java
index d60525d..d601a6a 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java
@@ -351,6 +351,8 @@
* <p>Mutually exclusive with all field and method properties as use restricts the match to both
* types of members.
*
+ * <p>Mutually exclusive with the property `memberFromBinding` also defining member-access.
+ *
* @return The member access-flag constraints that must be met.
*/
MemberAccessFlags[] memberAccess() default {};
@@ -363,6 +365,8 @@
* <p>If none, and other properties define this item as a method, the default matches any
* method-access flags.
*
+ * <p>Mutually exclusive with the property `memberFromBinding` also defining method-access.
+ *
* @return The method access-flag constraints that must be met.
*/
MethodAccessFlags[] methodAccess() default {};
@@ -375,11 +379,37 @@
* <p>If none, and other properties define this item as a method, the default matches any method
* name.
*
+ * <p>Mutually exclusive with the following other properties defining method-name:
+ *
+ * <ul>
+ * <li>methodNamePattern
+ * <li>memberFromBinding
+ * </ul>
+ *
* @return The exact method name of the method.
*/
String methodName() default "";
/**
+ * Define the method-name pattern by a string pattern.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any method
+ * name.
+ *
+ * <p>Mutually exclusive with the following other properties defining method-name:
+ *
+ * <ul>
+ * <li>methodName
+ * <li>memberFromBinding
+ * </ul>
+ *
+ * @return The string pattern of the method name.
+ */
+ StringPattern methodNamePattern() default @StringPattern(exact = "");
+
+ /**
* Define the method return-type pattern by a fully qualified type or 'void'.
*
* <p>Mutually exclusive with all field properties.
@@ -392,6 +422,7 @@
* <ul>
* <li>methodReturnTypeConstant
* <li>methodReturnTypePattern
+ * <li>memberFromBinding
* </ul>
*
* @return The qualified type name of the method return type.
@@ -411,6 +442,7 @@
* <ul>
* <li>methodReturnType
* <li>methodReturnTypePattern
+ * <li>memberFromBinding
* </ul>
*
* @return A class constant denoting the type of the method return type.
@@ -430,6 +462,7 @@
* <ul>
* <li>methodReturnType
* <li>methodReturnTypeConstant
+ * <li>memberFromBinding
* </ul>
*
* @return The pattern of the method return type.
@@ -444,7 +477,12 @@
* <p>If none, and other properties define this item as a method, the default matches any
* parameters.
*
- * <p>Mutually exclusive with the property `methodParameterTypePatterns` also defining parameters.
+ * <p>Mutually exclusive with the following other properties defining parameters:
+ *
+ * <ul>
+ * <li>methodParameterTypePatterns
+ * <li>memberFromBinding
+ * </ul>
*
* @return The list of qualified type names of the method parameters.
*/
@@ -458,7 +496,12 @@
* <p>If none, and other properties define this item as a method, the default matches any
* parameters.
*
- * <p>Mutually exclusive with the property `methodParameters` also defining parameters.
+ * <p>Mutually exclusive with the following other properties defining parameters:
+ *
+ * <ul>
+ * <li>methodParameters
+ * <li>memberFromBinding
+ * </ul>
*
* @return The list of type patterns for the method parameters.
*/
@@ -472,6 +515,8 @@
* <p>If none, and other properties define this item as a field, the default matches any
* field-access flags.
*
+ * <p>Mutually exclusive with the property `memberFromBinding` also defining field-access.
+ *
* @return The field access-flag constraints that must be met.
*/
FieldAccessFlags[] fieldAccess() default {};
@@ -484,6 +529,8 @@
* <p>If none, and other properties define this item as a field, the default matches any field
* name.
*
+ * <p>Mutually exclusive with the property `memberFromBinding` also defining field-name.
+ *
* @return The exact field name of the field.
*/
String fieldName() default "";
@@ -500,6 +547,7 @@
* <ul>
* <li>fieldTypeConstant
* <li>fieldTypePattern
+ * <li>memberFromBinding
* </ul>
*
* @return The qualified type name for the field type.
@@ -518,6 +566,7 @@
* <ul>
* <li>fieldType
* <li>fieldTypePattern
+ * <li>memberFromBinding
* </ul>
*
* @return The class constant for the field type.
@@ -536,6 +585,7 @@
* <ul>
* <li>fieldType
* <li>fieldTypeConstant
+ * <li>memberFromBinding
* </ul>
*
* @return The type pattern for the field type.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/StringPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/StringPattern.java
new file mode 100644
index 0000000..f2aec77
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/StringPattern.java
@@ -0,0 +1,56 @@
+// 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.
+
+// ***********************************************************************************
+// GENERATED FILE. DO NOT EDIT! See KeepItemAnnotationGenerator.java.
+// ***********************************************************************************
+
+package com.android.tools.r8.keepanno.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A pattern structure for matching strings.
+ *
+ * <p>If no properties are set, the default pattern matches any string.
+ */
+@Target(ElementType.ANNOTATION_TYPE)
+@Retention(RetentionPolicy.CLASS)
+public @interface StringPattern {
+
+ /**
+ * Exact string content.
+ *
+ * <p>For example, {@code "foo"} or {@code "java.lang.String"}.
+ *
+ * <p>Mutually exclusive with the following other properties defining string-exact-pattern:
+ *
+ * <ul>
+ * <li>startsWith
+ * <li>endsWith
+ * </ul>
+ */
+ String exact() default "";
+
+ /**
+ * Matches strings beginning with the given prefix.
+ *
+ * <p>For example, {@code "get"} to match strings such as {@code "getMyValue"}.
+ *
+ * <p>Mutually exclusive with the property `exact` also defining string-prefix-pattern.
+ */
+ String startsWith() default "";
+
+ /**
+ * Matches strings ending with the given suffix.
+ *
+ * <p>For example, {@code "Setter"} to match strings such as {@code "myValueSetter"}.
+ *
+ * <p>Mutually exclusive with the property `exact` also defining string-suffix-pattern.
+ */
+ String endsWith() default "";
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByNative.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByNative.java
index 149e51c..229d0f1 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByNative.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByNative.java
@@ -128,11 +128,27 @@
* <p>If none, and other properties define this item as a method, the default matches any method
* name.
*
+ * <p>Mutually exclusive with the property `methodNamePattern` also defining method-name.
+ *
* @return The exact method name of the method.
*/
String methodName() default "";
/**
+ * Define the method-name pattern by a string pattern.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any method
+ * name.
+ *
+ * <p>Mutually exclusive with the property `methodName` also defining method-name.
+ *
+ * @return The string pattern of the method name.
+ */
+ StringPattern methodNamePattern() default @StringPattern(exact = "");
+
+ /**
* Define the method return-type pattern by a fully qualified type or 'void'.
*
* <p>Mutually exclusive with all field properties.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByReflection.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByReflection.java
index 24073af..7b8fa8e 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByReflection.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByReflection.java
@@ -128,11 +128,27 @@
* <p>If none, and other properties define this item as a method, the default matches any method
* name.
*
+ * <p>Mutually exclusive with the property `methodNamePattern` also defining method-name.
+ *
* @return The exact method name of the method.
*/
String methodName() default "";
/**
+ * Define the method-name pattern by a string pattern.
+ *
+ * <p>Mutually exclusive with all field properties.
+ *
+ * <p>If none, and other properties define this item as a method, the default matches any method
+ * name.
+ *
+ * <p>Mutually exclusive with the property `methodName` also defining method-name.
+ *
+ * @return The string pattern of the method name.
+ */
+ StringPattern methodNamePattern() default @StringPattern(exact = "");
+
+ /**
* Define the method return-type pattern by a fully qualified type or 'void'.
*
* <p>Mutually exclusive with all field properties.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ArrayPropertyParser.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ArrayPropertyParser.java
new file mode 100644
index 0000000..3c6cd08
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ArrayPropertyParser.java
@@ -0,0 +1,76 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.keepanno.asm;
+
+import com.android.tools.r8.keepanno.ast.ParsingContext;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import org.objectweb.asm.AnnotationVisitor;
+
+public class ArrayPropertyParser<T, P> extends PropertyParserBase<List<T>, P> {
+
+ private final Function<ParsingContext, PropertyParser<T, P>> elementParser;
+ private List<T> values;
+
+ public ArrayPropertyParser(
+ ParsingContext parsingContext, Function<ParsingContext, PropertyParser<T, P>> elementParser) {
+ super(parsingContext);
+ this.elementParser = elementParser;
+ }
+
+ @Override
+ AnnotationVisitor tryPropertyArray(P property, String name, Consumer<List<T>> setValue) {
+ // The property name and type is forwarded to the element parser.
+ values = new ArrayList<>();
+ ParsingContext parsingContext = getParsingContext();
+ return new AnnotationVisitorBase(parsingContext) {
+
+ private PropertyParser<T, P> getParser() {
+ PropertyParser<T, P> parser = elementParser.apply(parsingContext);
+ getMapping().forEach(parser::setProperty);
+ return parser;
+ }
+
+ @Override
+ public void visitEnd() {
+ setValue.accept(values);
+ }
+
+ @Override
+ public void visit(String unusedName, Object value) {
+ if (!getParser().tryParse(name, value, values::add)) {
+ super.visit(name, value);
+ }
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String unusedName, String descriptor) {
+ AnnotationVisitor visitor = getParser().tryParseAnnotation(name, descriptor, values::add);
+ if (visitor != null) {
+ return visitor;
+ }
+ return super.visitAnnotation(name, descriptor);
+ }
+
+ @Override
+ public void visitEnum(String unusedName, String descriptor, String value) {
+ if (!getParser().tryParseEnum(name, descriptor, value, values::add)) {
+ super.visitEnum(name, descriptor, value);
+ }
+ }
+
+ @Override
+ public AnnotationVisitor visitArray(String unusedName) {
+ AnnotationVisitor visitor = getParser().tryParseArray(name, values::add);
+ if (visitor != null) {
+ return visitor;
+ }
+ return super.visitArray(name);
+ }
+ };
+ }
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassNameParser.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassNameParser.java
index d6c41ac..9137b3e 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassNameParser.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassNameParser.java
@@ -7,30 +7,69 @@
import com.android.tools.r8.keepanno.asm.ClassNameParser.ClassNameProperty;
import com.android.tools.r8.keepanno.asm.ClassSimpleNameParser.ClassSimpleNameProperty;
import com.android.tools.r8.keepanno.asm.PackageNameParser.PackageNameProperty;
+import com.android.tools.r8.keepanno.asm.TypeParser.TypeProperty;
import com.android.tools.r8.keepanno.ast.AnnotationConstants.ClassNamePattern;
import com.android.tools.r8.keepanno.ast.KeepPackagePattern;
import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern;
+import com.android.tools.r8.keepanno.ast.KeepTypePattern;
import com.android.tools.r8.keepanno.ast.KeepUnqualfiedClassNamePattern;
import com.android.tools.r8.keepanno.ast.ParsingContext;
import com.android.tools.r8.keepanno.ast.ParsingContext.AnnotationParsingContext;
+import com.android.tools.r8.keepanno.ast.ParsingContext.PropertyParsingContext;
import com.google.common.collect.ImmutableList;
import java.util.function.Consumer;
import org.objectweb.asm.AnnotationVisitor;
public class ClassNameParser
- extends PropertyParserBase<KeepQualifiedClassNamePattern, ClassNameProperty, ClassNameParser> {
+ extends PropertyParserBase<KeepQualifiedClassNamePattern, ClassNameProperty> {
public ClassNameParser(ParsingContext parsingContext) {
super(parsingContext);
}
public enum ClassNameProperty {
- PATTERN
+ PATTERN,
+ NAME,
+ CONSTANT,
}
@Override
- public ClassNameParser self() {
- return this;
+ boolean tryProperty(
+ ClassNameProperty property,
+ String name,
+ Object value,
+ Consumer<KeepQualifiedClassNamePattern> setValue) {
+ switch (property) {
+ case NAME:
+ return new TypeParser(getParsingContext())
+ .tryProperty(
+ TypeProperty.TYPE_NAME,
+ name,
+ value,
+ type -> setValue.accept(typeToClassType(type, getParsingContext().property(name))));
+ case CONSTANT:
+ return new TypeParser(getParsingContext())
+ .tryProperty(
+ TypeProperty.TYPE_CONSTANT,
+ name,
+ value,
+ type -> setValue.accept(typeToClassType(type, getParsingContext().property(name))));
+ default:
+ return false;
+ }
+ }
+
+ KeepQualifiedClassNamePattern typeToClassType(
+ KeepTypePattern typePattern, PropertyParsingContext parsingContext) {
+ return typePattern.match(
+ KeepQualifiedClassNamePattern::any,
+ primitiveTypePattern -> {
+ throw parsingContext.error("Invalid use of primitive type where class type was expected");
+ },
+ arrayTypePattern -> {
+ throw parsingContext.error("Invalid use of array type where class type was expected");
+ },
+ classNamePattern -> classNamePattern);
}
@Override
@@ -43,13 +82,11 @@
case PATTERN:
{
AnnotationParsingContext parsingContext =
- new AnnotationParsingContext(getParsingContext(), descriptor);
- PackageNameParser packageParser =
- new PackageNameParser(parsingContext)
- .setProperty(PackageNameProperty.NAME, ClassNamePattern.packageName);
- ClassSimpleNameParser nameParser =
- new ClassSimpleNameParser(parsingContext)
- .setProperty(ClassSimpleNameProperty.NAME, ClassNamePattern.simpleName);
+ getParsingContext().property(name).annotation(descriptor);
+ PackageNameParser packageParser = new PackageNameParser(parsingContext);
+ ClassSimpleNameParser nameParser = new ClassSimpleNameParser(parsingContext);
+ packageParser.setProperty(ClassNamePattern.packageName, PackageNameProperty.NAME);
+ nameParser.setProperty(ClassNamePattern.simpleName, ClassSimpleNameProperty.NAME);
return new ParserVisitor(
parsingContext,
descriptor,
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassSimpleNameParser.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassSimpleNameParser.java
index d349898..ed46972 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassSimpleNameParser.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassSimpleNameParser.java
@@ -10,8 +10,7 @@
import java.util.function.Consumer;
public class ClassSimpleNameParser
- extends PropertyParserBase<
- KeepUnqualfiedClassNamePattern, ClassSimpleNameProperty, ClassSimpleNameParser> {
+ extends PropertyParserBase<KeepUnqualfiedClassNamePattern, ClassSimpleNameProperty> {
public ClassSimpleNameParser(ParsingContext parsingContext) {
super(parsingContext);
@@ -22,11 +21,6 @@
}
@Override
- public ClassSimpleNameParser self() {
- return this;
- }
-
- @Override
public boolean tryProperty(
ClassSimpleNameProperty property,
String name,
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ConvertingPropertyParser.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ConvertingPropertyParser.java
new file mode 100644
index 0000000..bbc07f3
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ConvertingPropertyParser.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.keepanno.asm;
+
+import java.util.function.Consumer;
+import java.util.function.Function;
+import org.objectweb.asm.AnnotationVisitor;
+
+/**
+ * Abstract base parser to help converting a parser of one type to another via delegation.
+ *
+ * @param <T1> Type of input parser.
+ * @param <T2> Type of output parser (e.g., the parser subclassing this base).
+ * @param <P> Properties (same for both parsers).
+ */
+public abstract class ConvertingPropertyParser<T1, T2, P> implements PropertyParser<T2, P> {
+
+ private final PropertyParser<T1, P> parser;
+ private final Function<T1, T2> converter;
+
+ public ConvertingPropertyParser(PropertyParser<T1, P> parser, Function<T1, T2> converter) {
+ this.parser = parser;
+ this.converter = converter;
+ }
+
+ private Consumer<T1> wrap(Consumer<T2> fn) {
+ return v1 -> fn.accept(converter.apply(v1));
+ }
+
+ @Override
+ public void setProperty(String name, P property) {
+ parser.setProperty(name, property);
+ }
+
+ @Override
+ public boolean isDeclared() {
+ return parser.isDeclared();
+ }
+
+ @Override
+ public T2 getValue() {
+ return converter.apply(parser.getValue());
+ }
+
+ @Override
+ public T2 tryParse(String name, Object value) {
+ T1 t1 = parser.tryParse(name, value);
+ return t1 == null ? null : converter.apply(t1);
+ }
+
+ @Override
+ public boolean tryParseEnum(String name, String descriptor, String value, Consumer<T2> setValue) {
+ return parser.tryParseEnum(name, descriptor, value, wrap(setValue));
+ }
+
+ @Override
+ public AnnotationVisitor tryParseArray(String name, Consumer<T2> setValue) {
+ return parser.tryParseArray(name, wrap(setValue));
+ }
+
+ @Override
+ public AnnotationVisitor tryParseAnnotation(
+ String name, String descriptor, Consumer<T2> setValue) {
+ return parser.tryParseAnnotation(name, descriptor, wrap(setValue));
+ }
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/FieldTypeParser.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/FieldTypeParser.java
new file mode 100644
index 0000000..176b1cd
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/FieldTypeParser.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.keepanno.asm;
+
+import com.android.tools.r8.keepanno.asm.TypeParser.TypeProperty;
+import com.android.tools.r8.keepanno.ast.KeepFieldTypePattern;
+import com.android.tools.r8.keepanno.ast.KeepTypePattern;
+import com.android.tools.r8.keepanno.ast.ParsingContext;
+
+public class FieldTypeParser
+ extends ConvertingPropertyParser<KeepTypePattern, KeepFieldTypePattern, TypeProperty> {
+
+ public FieldTypeParser(ParsingContext parsingContext) {
+ super(new TypeParser(parsingContext), KeepFieldTypePattern::fromType);
+ }
+}
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 00729cb..7f03de2 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
@@ -3,10 +3,12 @@
// 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.ClassNameParser.ClassNameProperty;
+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;
import com.android.tools.r8.keepanno.ast.AnnotationConstants;
import com.android.tools.r8.keepanno.ast.AnnotationConstants.Binding;
-import com.android.tools.r8.keepanno.ast.AnnotationConstants.ClassNamePattern;
import com.android.tools.r8.keepanno.ast.AnnotationConstants.Condition;
import com.android.tools.r8.keepanno.ast.AnnotationConstants.Constraints;
import com.android.tools.r8.keepanno.ast.AnnotationConstants.Edge;
@@ -18,9 +20,7 @@
import com.android.tools.r8.keepanno.ast.AnnotationConstants.MethodAccess;
import com.android.tools.r8.keepanno.ast.AnnotationConstants.Option;
import com.android.tools.r8.keepanno.ast.AnnotationConstants.Target;
-import com.android.tools.r8.keepanno.ast.AnnotationConstants.TypePattern;
import com.android.tools.r8.keepanno.ast.AnnotationConstants.UsedByReflection;
-import com.android.tools.r8.keepanno.ast.KeepAnnotationParserException;
import com.android.tools.r8.keepanno.ast.KeepBindingReference;
import com.android.tools.r8.keepanno.ast.KeepBindings;
import com.android.tools.r8.keepanno.ast.KeepBindings.KeepBindingSymbol;
@@ -51,18 +51,17 @@
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.KeepPackagePattern;
import com.android.tools.r8.keepanno.ast.KeepPreconditions;
import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern;
+import com.android.tools.r8.keepanno.ast.KeepStringPattern;
import com.android.tools.r8.keepanno.ast.KeepTarget;
import com.android.tools.r8.keepanno.ast.KeepTypePattern;
-import com.android.tools.r8.keepanno.ast.KeepUnqualfiedClassNamePattern;
import com.android.tools.r8.keepanno.ast.ParsingContext;
import com.android.tools.r8.keepanno.ast.ParsingContext.AnnotationParsingContext;
import com.android.tools.r8.keepanno.ast.ParsingContext.ClassParsingContext;
import com.android.tools.r8.keepanno.ast.ParsingContext.FieldParsingContext;
+import com.android.tools.r8.keepanno.ast.ParsingContext.GroupParsingContext;
import com.android.tools.r8.keepanno.ast.ParsingContext.MethodParsingContext;
-import com.android.tools.r8.keepanno.utils.Unimplemented;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
@@ -260,7 +259,6 @@
if (descriptor.equals(AnnotationConstants.CheckRemoved.DESCRIPTOR)) {
return new CheckRemovedClassVisitor(
annotationParsingContext(descriptor),
- descriptor,
parent::accept,
this::setContext,
className,
@@ -269,7 +267,6 @@
if (descriptor.equals(AnnotationConstants.CheckOptimizedOut.DESCRIPTOR)) {
return new CheckRemovedClassVisitor(
annotationParsingContext(descriptor),
- descriptor,
parent::accept,
this::setContext,
className,
@@ -381,7 +378,6 @@
if (descriptor.equals(AnnotationConstants.CheckRemoved.DESCRIPTOR)) {
return new CheckRemovedMemberVisitor(
annotationParsingContext(descriptor),
- descriptor,
parent::accept,
this::setContext,
createMethodItemContext(),
@@ -390,7 +386,6 @@
if (descriptor.equals(AnnotationConstants.CheckOptimizedOut.DESCRIPTOR)) {
return new CheckRemovedMemberVisitor(
annotationParsingContext(descriptor),
- descriptor,
parent::accept,
this::setContext,
createMethodItemContext(),
@@ -863,7 +858,6 @@
private static class UsedByReflectionMemberVisitor extends AnnotationVisitorBase {
private final AnnotationParsingContext parsingContext;
- private final String annotationDescriptor;
private final Parent<KeepEdge> parent;
private final KeepItemPattern context;
private final KeepEdge.Builder builder = KeepEdge.builder();
@@ -881,7 +875,6 @@
KeepItemPattern context) {
super(parsingContext);
this.parsingContext = parsingContext;
- this.annotationDescriptor = annotationDescriptor;
this.parent = parent;
this.context = context;
addContext.accept(metaInfoBuilder);
@@ -1110,7 +1103,6 @@
private static class CheckRemovedClassVisitor extends AnnotationVisitorBase {
private final AnnotationParsingContext parsingContext;
- private final String annotationDescriptor;
private final Parent<KeepCheck> parent;
private final KeepEdgeMetaInfo.Builder metaInfoBuilder = KeepEdgeMetaInfo.builder();
private final String className;
@@ -1118,14 +1110,12 @@
public CheckRemovedClassVisitor(
AnnotationParsingContext parsingContext,
- String annotationDescriptor,
Parent<KeepCheck> parent,
Consumer<KeepEdgeMetaInfo.Builder> addContext,
String className,
KeepCheckKind kind) {
super(parsingContext);
this.parsingContext = parsingContext;
- this.annotationDescriptor = annotationDescriptor;
this.parent = parent;
this.className = className;
this.kind = kind;
@@ -1143,7 +1133,6 @@
@Override
public void visitEnd() {
- CheckRemovedClassVisitor superVisitor = this;
KeepItemVisitorBase itemVisitor =
new KeepItemVisitorBase(parsingContext) {
@Override
@@ -1165,7 +1154,6 @@
/** Parsing of @CheckRemoved and @CheckOptimizedOut on a class context. */
private static class CheckRemovedMemberVisitor extends AnnotationVisitorBase {
- private final String annotationDescriptor;
private final Parent<KeepDeclaration> parent;
private final KeepItemPattern context;
private final KeepEdgeMetaInfo.Builder metaInfoBuilder = KeepEdgeMetaInfo.builder();
@@ -1173,13 +1161,11 @@
CheckRemovedMemberVisitor(
AnnotationParsingContext parsingContext,
- String annotationDescriptor,
Parent<KeepDeclaration> parent,
Consumer<KeepEdgeMetaInfo.Builder> addContext,
KeepItemPattern context,
KeepCheckKind kind) {
super(parsingContext);
- this.annotationDescriptor = annotationDescriptor;
this.parent = parent;
this.context = context;
this.kind = kind;
@@ -1208,7 +1194,6 @@
}
abstract static class Declaration<T> {
- abstract String kind();
boolean isDefault() {
for (Declaration<?> declaration : declarations()) {
@@ -1219,18 +1204,27 @@
return true;
}
- abstract T getValue();
-
List<Declaration<?>> declarations() {
return Collections.emptyList();
}
+ List<PropertyParser<?, ?>> parsers() {
+ return Collections.emptyList();
+ }
+
+ private void ignore(Object arg) {}
+
boolean tryParse(String name, Object value) {
for (Declaration<?> declaration : declarations()) {
if (declaration.tryParse(name, value)) {
return true;
}
}
+ for (PropertyParser<?, ?> parser : parsers()) {
+ if (parser.tryParse(name, value, this::ignore)) {
+ return true;
+ }
+ }
return false;
}
@@ -1241,6 +1235,12 @@
return visitor;
}
}
+ for (PropertyParser<?, ?> parser : parsers()) {
+ AnnotationVisitor visitor = parser.tryParseArray(name, this::ignore);
+ if (visitor != null) {
+ return visitor;
+ }
+ }
return null;
}
@@ -1251,6 +1251,12 @@
return visitor;
}
}
+ for (PropertyParser<?, ?> parser : parsers()) {
+ AnnotationVisitor visitor = parser.tryParseAnnotation(name, descriptor, this::ignore);
+ if (visitor != null) {
+ return visitor;
+ }
+ }
return null;
}
}
@@ -1261,7 +1267,7 @@
private T declarationValue = null;
private AnnotationVisitor declarationVisitor = null;
- private SingleDeclaration(ParsingContext parsingContext) {
+ private SingleDeclaration(GroupParsingContext parsingContext) {
this.parsingContext = parsingContext;
}
@@ -1291,19 +1297,11 @@
}
private void error(String name) {
- throw new KeepAnnotationParserException(
- parsingContext,
- "Multiple declarations defining "
- + kind()
- + ": '"
- + declarationName
- + "' and '"
- + name
- + "'");
+ throw parsingContext.error(
+ "Multiple properties: '" + declarationName + "' and '" + name + "'");
}
- @Override
- public final T getValue() {
+ final T getValue() {
return declarationValue == null ? getDefaultValue() : declarationValue;
}
@@ -1350,54 +1348,10 @@
}
}
- private static class ClassNameDeclaration
- extends SingleDeclaration<KeepQualifiedClassNamePattern> {
-
- private ClassNameDeclaration(ParsingContext parsingContext) {
- super(parsingContext);
- }
-
- @Override
- String kind() {
- return "class-name";
- }
-
- @Override
- KeepQualifiedClassNamePattern getDefaultValue() {
- return KeepQualifiedClassNamePattern.any();
- }
-
- @Override
- KeepQualifiedClassNamePattern parse(String name, Object value) {
- if (name.equals(Item.classConstant) && value instanceof Type) {
- return KeepQualifiedClassNamePattern.exact(((Type) value).getClassName());
- }
- if (name.equals(Item.className) && value instanceof String) {
- return KeepQualifiedClassNamePattern.exact(((String) value));
- }
- return null;
- }
-
- @Override
- AnnotationVisitor parseAnnotation(
- String name, String descriptor, Consumer<KeepQualifiedClassNamePattern> setValue) {
- if (name.equals(Item.classNamePattern) && descriptor.equals(ClassNamePattern.DESCRIPTOR)) {
- return new ClassNamePatternVisitor(
- new AnnotationParsingContext(getParsingContext(), descriptor), setValue);
- }
- return super.parseAnnotation(name, descriptor, setValue);
- }
- }
-
private static class InstanceOfDeclaration extends SingleDeclaration<KeepInstanceOfPattern> {
private InstanceOfDeclaration(ParsingContext parsingContext) {
- super(parsingContext);
- }
-
- @Override
- String kind() {
- return "instance-of";
+ super(parsingContext.group(Item.instanceOfGroup));
}
@Override
@@ -1451,17 +1405,23 @@
private final Supplier<UserBindingsHelper> getBindingsHelper;
private KeepClassItemReference boundClassItemReference = null;
- private final ClassNameDeclaration classNameDeclaration;
+ private final ClassNameParser classNameParser;
private final InstanceOfDeclaration instanceOfDeclaration;
private final List<Declaration<?>> declarations;
+ private final List<PropertyParser<?, ?>> parsers;
public ClassDeclaration(
ParsingContext parsingContext, Supplier<UserBindingsHelper> getBindingsHelper) {
- this.parsingContext = parsingContext;
+ this.parsingContext = parsingContext.group(Item.classGroup);
this.getBindingsHelper = getBindingsHelper;
- classNameDeclaration = new ClassNameDeclaration(parsingContext);
+ classNameParser = new ClassNameParser(parsingContext.group(Item.classNameGroup));
+ classNameParser.setProperty(Item.className, ClassNameProperty.NAME);
+ classNameParser.setProperty(Item.classConstant, ClassNameProperty.CONSTANT);
+ classNameParser.setProperty(Item.classNamePattern, ClassNameProperty.PATTERN);
+ parsers = ImmutableList.of(classNameParser);
+
instanceOfDeclaration = new InstanceOfDeclaration(parsingContext);
- declarations = ImmutableList.of(classNameDeclaration, instanceOfDeclaration);
+ declarations = ImmutableList.of(instanceOfDeclaration);
}
@Override
@@ -1469,12 +1429,17 @@
return declarations;
}
+ @Override
+ List<PropertyParser<?, ?>> parsers() {
+ return parsers;
+ }
+
private boolean isBindingReferenceDefined() {
return boundClassItemReference != null;
}
private boolean classPatternsAreDefined() {
- return !classNameDeclaration.isDefault() || !instanceOfDeclaration.isDefault();
+ return !classNameParser.isDefault() || !instanceOfDeclaration.isDefault();
}
private void checkAllowedDefinitions() {
@@ -1485,24 +1450,19 @@
}
@Override
- String kind() {
- return "class";
- }
-
- @Override
boolean isDefault() {
return !isBindingReferenceDefined() && super.isDefault();
}
- @Override
- KeepClassItemReference getValue() {
+ private KeepClassItemReference getValue() {
checkAllowedDefinitions();
if (isBindingReferenceDefined()) {
return boundClassItemReference;
}
if (classPatternsAreDefined()) {
return KeepClassItemPattern.builder()
- .setClassNamePattern(classNameDeclaration.getValue())
+ .setClassNamePattern(
+ classNameParser.getValueOrDefault(KeepQualifiedClassNamePattern.any()))
.setInstanceOfPattern(instanceOfDeclaration.getValue())
.build()
.toClassItemReference();
@@ -1530,139 +1490,39 @@
}
}
- private static class MethodReturnTypeDeclaration
- extends SingleDeclaration<KeepMethodReturnTypePattern> {
-
- private final TypeParser typeParser;
-
- private MethodReturnTypeDeclaration(ParsingContext parsingContext) {
- super(parsingContext);
- typeParser =
- new TypeParser(parsingContext)
- .setKind("return type")
- .enableTypePattern(Item.methodReturnTypePattern)
- .enableTypeName(Item.methodReturnType)
- .enableTypeConstant(Item.methodReturnTypeConstant);
- }
-
- @Override
- String kind() {
- return "return type";
- }
-
- @Override
- KeepMethodReturnTypePattern getDefaultValue() {
- return KeepMethodReturnTypePattern.any();
- }
-
- KeepMethodReturnTypePattern fromType(KeepTypePattern typePattern) {
- if (typePattern == null) {
- return null;
- }
- // Special-case method return types to allow void.
- String descriptor = typePattern.getDescriptor();
- if (descriptor.equals("V") || descriptor.equals("Lvoid;")) {
- return KeepMethodReturnTypePattern.voidType();
- }
- return KeepMethodReturnTypePattern.fromType(typePattern);
- }
-
- @Override
- public KeepMethodReturnTypePattern parse(String name, Object value) {
- return fromType(typeParser.tryParse(name, value));
- }
-
- @Override
- public AnnotationVisitor parseAnnotation(
- String name, String descriptor, Consumer<KeepMethodReturnTypePattern> setValue) {
- return typeParser.tryParseAnnotation(name, descriptor, t -> setValue.accept(fromType(t)));
- }
- }
-
- private static class MethodParametersDeclaration
- extends SingleDeclaration<KeepMethodParametersPattern> {
-
- private final ParsingContext parsingContext;
- private KeepMethodParametersPattern pattern = null;
-
- public MethodParametersDeclaration(ParsingContext parsingContext) {
- super(parsingContext);
- this.parsingContext = parsingContext;
- }
-
- private void setPattern(
- KeepMethodParametersPattern pattern, Consumer<KeepMethodParametersPattern> setValue) {
- assert setValue != null;
- if (this.pattern != null) {
- throw parsingContext.error("Cannot declare multiple patterns for the parameter list");
- }
- setValue.accept(pattern);
- this.pattern = pattern;
- }
-
- @Override
- String kind() {
- return "parameters";
- }
-
- @Override
- KeepMethodParametersPattern getDefaultValue() {
- return KeepMethodParametersPattern.any();
- }
-
- @Override
- KeepMethodParametersPattern parse(String name, Object value) {
- return null;
- }
-
- @Override
- AnnotationVisitor parseArray(String name, Consumer<KeepMethodParametersPattern> setValue) {
- if (name.equals(Item.methodParameters)) {
- return new StringArrayVisitor(
- getParsingContext(),
- params -> {
- KeepMethodParametersPattern.Builder builder = KeepMethodParametersPattern.builder();
- for (String param : params) {
- builder.addParameterTypePattern(KeepEdgeReaderUtils.typePatternFromString(param));
- }
- setPattern(builder.build(), setValue);
- });
- }
- if (name.equals(Item.methodParameterTypePatterns)) {
- return new TypePatternsArrayVisitor(
- getParsingContext(),
- params -> {
- KeepMethodParametersPattern.Builder builder = KeepMethodParametersPattern.builder();
- for (KeepTypePattern param : params) {
- builder.addParameterTypePattern(param);
- }
- setPattern(builder.build(), setValue);
- });
- }
- return super.parseArray(name, setValue);
- }
- }
-
private static class MethodDeclaration extends Declaration<KeepMethodPattern> {
private final ParsingContext parsingContext;
private KeepMethodAccessPattern.Builder accessBuilder = null;
private KeepMethodPattern.Builder builder = null;
- private final MethodReturnTypeDeclaration returnTypeDeclaration;
- private final MethodParametersDeclaration parametersDeclaration;
+ private final StringPatternParser nameParser;
+ private final MethodReturnTypeParser returnTypeParser;
+ private final MethodParametersParser parametersParser;
- private final List<Declaration<?>> declarations;
+ private final List<PropertyParser<?, ?>> parsers;
private MethodDeclaration(ParsingContext parsingContext) {
this.parsingContext = parsingContext;
- returnTypeDeclaration = new MethodReturnTypeDeclaration(parsingContext);
- parametersDeclaration = new MethodParametersDeclaration(parsingContext);
- declarations = ImmutableList.of(returnTypeDeclaration, parametersDeclaration);
+
+ nameParser = new StringPatternParser(parsingContext.group(Item.methodNameGroup));
+ nameParser.setProperty(Item.methodName, StringProperty.EXACT);
+ nameParser.setProperty(Item.methodNamePattern, StringProperty.PATTERN);
+
+ returnTypeParser = new MethodReturnTypeParser(parsingContext.group(Item.returnTypeGroup));
+ returnTypeParser.setProperty(Item.methodReturnType, TypeProperty.TYPE_NAME);
+ returnTypeParser.setProperty(Item.methodReturnTypeConstant, TypeProperty.TYPE_CONSTANT);
+ returnTypeParser.setProperty(Item.methodReturnTypePattern, TypeProperty.TYPE_PATTERN);
+
+ parametersParser = new MethodParametersParser(parsingContext.group(Item.parametersGroup));
+ parametersParser.setProperty(Item.methodParameters, TypeProperty.TYPE_NAME);
+ parametersParser.setProperty(Item.methodParameterTypePatterns, TypeProperty.TYPE_PATTERN);
+
+ parsers = ImmutableList.of(nameParser, returnTypeParser, parametersParser);
}
@Override
- List<Declaration<?>> declarations() {
- return declarations;
+ List<PropertyParser<?, ?>> parsers() {
+ return parsers;
}
private KeepMethodPattern.Builder getBuilder() {
@@ -1673,39 +1533,28 @@
}
@Override
- String kind() {
- return "method";
- }
-
- @Override
boolean isDefault() {
return accessBuilder == null && builder == null && super.isDefault();
}
- @Override
- KeepMethodPattern getValue() {
+ private KeepMethodPattern getValue() {
if (accessBuilder != null) {
getBuilder().setAccessPattern(accessBuilder.build());
}
- if (!returnTypeDeclaration.isDefault()) {
- getBuilder().setReturnTypePattern(returnTypeDeclaration.getValue());
+ if (!nameParser.isDefault()) {
+ KeepStringPattern namePattern = nameParser.getValue();
+ getBuilder().setNamePattern(KeepMethodNamePattern.fromStringPattern(namePattern));
}
- if (!parametersDeclaration.isDefault()) {
- getBuilder().setParametersPattern(parametersDeclaration.getValue());
+ if (!returnTypeParser.isDefault()) {
+ getBuilder().setReturnTypePattern(returnTypeParser.getValue());
+ }
+ if (!parametersParser.isDefault()) {
+ getBuilder().setParametersPattern(parametersParser.getValue());
}
return builder != null ? builder.build() : null;
}
@Override
- boolean tryParse(String name, Object value) {
- if (name.equals(Item.methodName) && value instanceof String) {
- getBuilder().setNamePattern(KeepMethodNamePattern.exact((String) value));
- return true;
- }
- return super.tryParse(name, value);
- }
-
- @Override
AnnotationVisitor tryParseArray(String name) {
if (name.equals(Item.methodAccess)) {
accessBuilder = KeepMethodAccessPattern.builder();
@@ -1715,64 +1564,27 @@
}
}
- private static class FieldTypeDeclaration extends SingleDeclaration<KeepFieldTypePattern> {
-
- private final TypeParser typeParser;
-
- private FieldTypeDeclaration(ParsingContext parsingContext) {
- super(parsingContext);
- this.typeParser =
- new TypeParser(parsingContext)
- .setKind("field type")
- .enableTypePattern(Item.fieldTypePattern)
- .enableTypeName(Item.fieldType)
- .enableTypeConstant(Item.fieldTypeConstant);
- }
-
- @Override
- String kind() {
- return "field type";
- }
-
- @Override
- KeepFieldTypePattern getDefaultValue() {
- return KeepFieldTypePattern.any();
- }
-
- @Override
- public KeepFieldTypePattern parse(String name, Object value) {
- KeepTypePattern typePattern = typeParser.tryParse(name, value);
- if (typePattern != null) {
- return KeepFieldTypePattern.fromType(typePattern);
- }
- return null;
- }
-
- @Override
- public AnnotationVisitor parseAnnotation(
- String name, String descriptor, Consumer<KeepFieldTypePattern> setValue) {
- return typeParser.tryParseAnnotation(
- name, descriptor, t -> setValue.accept(KeepFieldTypePattern.fromType(t)));
- }
- }
-
private static class FieldDeclaration extends Declaration<KeepFieldPattern> {
private final ParsingContext parsingContext;
- private final FieldTypeDeclaration typeDeclaration;
+ private final FieldTypeParser typeParser;
private KeepFieldAccessPattern.Builder accessBuilder = null;
private KeepFieldPattern.Builder builder = null;
- private final List<Declaration<?>> declarations;
+ private final List<PropertyParser<?, ?>> parsers;
public FieldDeclaration(ParsingContext parsingContext) {
this.parsingContext = parsingContext;
- typeDeclaration = new FieldTypeDeclaration(parsingContext);
- declarations = Collections.singletonList(typeDeclaration);
+ typeParser = new FieldTypeParser(parsingContext.group(Item.fieldTypeGroup));
+ typeParser.setProperty(Item.fieldTypePattern, TypeProperty.TYPE_PATTERN);
+ typeParser.setProperty(Item.fieldType, TypeProperty.TYPE_NAME);
+ typeParser.setProperty(Item.fieldTypeConstant, TypeProperty.TYPE_CONSTANT);
+
+ parsers = Collections.singletonList(typeParser);
}
@Override
- List<Declaration<?>> declarations() {
- return declarations;
+ List<PropertyParser<?, ?>> parsers() {
+ return parsers;
}
private KeepFieldPattern.Builder getBuilder() {
@@ -1783,22 +1595,16 @@
}
@Override
- String kind() {
- return "field";
- }
-
- @Override
boolean isDefault() {
return accessBuilder == null && builder == null;
}
- @Override
- KeepFieldPattern getValue() {
+ private KeepFieldPattern getValue() {
if (accessBuilder != null) {
getBuilder().setAccessPattern(accessBuilder.build());
}
- if (!typeDeclaration.isDefault()) {
- getBuilder().setTypePattern(typeDeclaration.getValue());
+ if (!typeParser.isDefault()) {
+ getBuilder().setTypePattern(typeParser.getValue());
}
return builder != null ? builder.build() : null;
}
@@ -1831,7 +1637,7 @@
private final List<Declaration<?>> declarations;
MemberDeclaration(ParsingContext parsingContext) {
- this.parsingContext = parsingContext;
+ this.parsingContext = parsingContext.group(Item.memberGroup);
methodDeclaration = new MethodDeclaration(parsingContext);
fieldDeclaration = new FieldDeclaration(parsingContext);
declarations = ImmutableList.of(methodDeclaration, fieldDeclaration);
@@ -1843,17 +1649,11 @@
}
@Override
- String kind() {
- return "member";
- }
-
- @Override
public boolean isDefault() {
return accessBuilder == null && methodDeclaration.isDefault() && fieldDeclaration.isDefault();
}
- @Override
- public KeepMemberPattern getValue() {
+ private KeepMemberPattern getValue() {
KeepMethodPattern method = methodDeclaration.getValue();
KeepFieldPattern field = fieldDeclaration.getValue();
if (accessBuilder != null) {
@@ -2175,237 +1975,10 @@
}
}
- private static class StringArrayVisitor extends AnnotationVisitorBase {
- private final Consumer<List<String>> fn;
- private final List<String> strings = new ArrayList<>();
-
- public StringArrayVisitor(ParsingContext parsingContext, Consumer<List<String>> fn) {
- super(parsingContext);
- this.fn = fn;
- }
-
- @Override
- public void visit(String name, Object value) {
- if (value instanceof String) {
- strings.add((String) value);
- } else {
- super.visit(name, value);
- }
- }
-
- @Override
- public void visitEnd() {
- super.visitEnd();
- fn.accept(strings);
- }
- }
-
- private static class ClassSimpleNameDeclaration
- extends SingleDeclaration<KeepUnqualfiedClassNamePattern> {
-
- private ClassSimpleNameDeclaration(ParsingContext parsingContext) {
- super(parsingContext);
- }
-
- @Override
- String kind() {
- return "class-simple-name";
- }
-
- @Override
- KeepUnqualfiedClassNamePattern getDefaultValue() {
- return KeepUnqualfiedClassNamePattern.any();
- }
-
- @Override
- KeepUnqualfiedClassNamePattern parse(String name, Object value) {
- if (name.equals(ClassNamePattern.simpleName) && value instanceof String) {
- return KeepUnqualfiedClassNamePattern.builder().exact((String) value).build();
- }
- return null;
- }
- }
-
- private static class PackageDeclaration extends SingleDeclaration<KeepPackagePattern> {
-
- private PackageDeclaration(ParsingContext parsingContext) {
- super(parsingContext);
- }
-
- @Override
- String kind() {
- return "package";
- }
-
- @Override
- KeepPackagePattern getDefaultValue() {
- return KeepPackagePattern.any();
- }
-
- @Override
- KeepPackagePattern parse(String name, Object value) {
- if (name.equals(ClassNamePattern.packageName) && value instanceof String) {
- return KeepPackagePattern.builder().exact((String) value).build();
- }
- return null;
- }
- }
-
- private static class ClassNamePatternDeclaration
- extends Declaration<KeepQualifiedClassNamePattern> {
-
- private final ClassSimpleNameDeclaration nameDeclaration;
- private final PackageDeclaration packageDeclaration;
- private final List<Declaration<?>> declarations;
-
- public ClassNamePatternDeclaration(ParsingContext parsingContext) {
- nameDeclaration = new ClassSimpleNameDeclaration(parsingContext);
- packageDeclaration = new PackageDeclaration(parsingContext);
- declarations = ImmutableList.of(nameDeclaration, packageDeclaration);
- }
-
- @Override
- String kind() {
- return "class-name";
- }
-
- @Override
- KeepQualifiedClassNamePattern getValue() {
- if (!packageDeclaration.isDefault() || !nameDeclaration.isDefault()) {
- return KeepQualifiedClassNamePattern.builder()
- .setPackagePattern(packageDeclaration.getValue())
- .setNamePattern(nameDeclaration.getValue())
- .build();
- }
- return null;
- }
-
- @Override
- List<Declaration<?>> declarations() {
- return declarations;
- }
- }
-
- private static class ClassNamePatternVisitor extends AnnotationVisitorBase {
-
- private final ClassNamePatternDeclaration declaration;
- private final Consumer<KeepQualifiedClassNamePattern> setValue;
-
- public ClassNamePatternVisitor(
- AnnotationParsingContext parsingContext, Consumer<KeepQualifiedClassNamePattern> setValue) {
- super(parsingContext);
- this.setValue = setValue;
- declaration = new ClassNamePatternDeclaration(parsingContext);
- }
-
- @Override
- public void visit(String name, Object value) {
- if (!declaration.tryParse(name, value)) {
- super.visit(name, value);
- }
- }
-
- @Override
- public void visitEnd() {
- if (!declaration.isDefault()) {
- setValue.accept(declaration.getValue());
- }
- super.visitEnd();
- }
- }
-
- private static class TypePatternVisitor extends AnnotationVisitorBase {
- private final ParsingContext parsingContext;
- private final Consumer<KeepTypePattern> consumer;
- private KeepTypePattern result = null;
-
- private TypePatternVisitor(ParsingContext parsingContext, Consumer<KeepTypePattern> consumer) {
- super(parsingContext);
- this.parsingContext = parsingContext;
- this.consumer = consumer;
- }
-
- private void setResult(KeepTypePattern result) {
- if (this.result != null) {
- throw parsingContext.error("Invalid type annotation defining multiple properties.");
- }
- this.result = result;
- }
-
- @Override
- public void visit(String name, Object value) {
- if (TypePattern.name.equals(name) && value instanceof String) {
- setResult(KeepEdgeReaderUtils.typePatternFromString((String) value));
- return;
- }
- if (TypePattern.constant.equals(name) && value instanceof Type) {
- Type type = (Type) value;
- setResult(KeepTypePattern.fromDescriptor(type.getDescriptor()));
- return;
- }
- super.visit(name, value);
- }
-
- @Override
- public AnnotationVisitor visitAnnotation(String name, String descriptor) {
- if (TypePattern.classNamePattern.equals(name)
- && descriptor.equals(ClassNamePattern.DESCRIPTOR)) {
- return new ClassNamePatternVisitor(
- new AnnotationParsingContext(parsingContext, descriptor),
- p -> {
- if (p.isExact()) {
- setResult(KeepTypePattern.fromDescriptor(p.getExactDescriptor()));
- } else {
- // TODO(b/248408342): Extend the AST type patterns.
- throw new Unimplemented("Non-exact class patterns are not implemented yet");
- }
- });
- }
- return super.visitAnnotation(name, descriptor);
- }
-
- @Override
- public void visitEnd() {
- consumer.accept(result != null ? result : KeepTypePattern.any());
- }
- }
-
- private static class TypePatternsArrayVisitor extends AnnotationVisitorBase {
- private final ParsingContext parsingContext;
- private final Consumer<List<KeepTypePattern>> fn;
- private final List<KeepTypePattern> patterns = new ArrayList<>();
-
- public TypePatternsArrayVisitor(
- ParsingContext parsingContext, Consumer<List<KeepTypePattern>> fn) {
- super(parsingContext);
- this.parsingContext = parsingContext;
- this.fn = fn;
- }
-
- @Override
- public AnnotationVisitor visitAnnotation(String unusedName, String descriptor) {
- if (TypePattern.DESCRIPTOR.equals(descriptor)) {
- return new TypePatternVisitor(parsingContext, patterns::add);
- }
- return null;
- }
-
- @Override
- public void visitEnd() {
- super.visitEnd();
- fn.accept(patterns);
- }
- }
-
private static class OptionsDeclaration extends SingleDeclaration<KeepOptions> {
public OptionsDeclaration(ParsingContext parsingContext) {
- super(parsingContext);
- }
-
- @Override
- String kind() {
- return "options";
+ super(parsingContext.group(Target.constraintsGroup));
}
@Override
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReaderUtils.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReaderUtils.java
index 18e5c02..21645d5 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReaderUtils.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReaderUtils.java
@@ -3,9 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.keepanno.asm;
-import com.android.tools.r8.keepanno.ast.KeepEdgeException;
-import com.android.tools.r8.keepanno.ast.KeepMethodReturnTypePattern;
import com.android.tools.r8.keepanno.ast.KeepTypePattern;
+import com.android.tools.r8.keepanno.ast.ParsingContext.PropertyParsingContext;
+import java.util.function.Function;
/**
* Utilities for mapping the syntax used in annotations to the keep-edge AST.
@@ -57,14 +57,20 @@
throw new IllegalStateException("Unexpected descriptor: " + descriptor);
}
- public static KeepTypePattern typePatternFromString(String string) {
+ public static KeepTypePattern typePatternFromString(
+ String string, PropertyParsingContext property) {
if (string.equals("<any>")) {
return KeepTypePattern.any();
}
- return KeepTypePattern.fromDescriptor(getDescriptorFromJavaType(string));
+ return KeepTypePattern.fromDescriptor(internalDescriptorFromJavaType(string, property::error));
}
public static String getDescriptorFromJavaType(String type) {
+ return internalDescriptorFromJavaType(type, IllegalStateException::new);
+ }
+
+ private static String internalDescriptorFromJavaType(
+ String type, Function<String, RuntimeException> onError) {
switch (type) {
case "boolean":
return "Z";
@@ -86,9 +92,12 @@
{
StringBuilder builder = new StringBuilder(type.length());
int i = type.length() - 1;
+ if (i < 0) {
+ throw onError.apply("Invalid empty type");
+ }
while (type.charAt(i) == ']') {
if (type.charAt(--i) != '[') {
- throw new KeepEdgeException("Invalid type: " + type);
+ throw onError.apply("Invalid type: '" + type + "'");
}
builder.append('[');
--i;
@@ -103,19 +112,4 @@
}
}
}
-
- public static KeepMethodReturnTypePattern methodReturnTypeFromTypeName(String returnType) {
- if ("void".equals(returnType)) {
- return KeepMethodReturnTypePattern.voidType();
- }
- return KeepMethodReturnTypePattern.fromType(typePatternFromString(returnType));
- }
-
- public static KeepMethodReturnTypePattern methodReturnTypeFromTypeDescriptor(
- String returnTypeDesc) {
- if ("V".equals(returnTypeDesc)) {
- return KeepMethodReturnTypePattern.voidType();
- }
- return KeepMethodReturnTypePattern.fromType(KeepTypePattern.fromDescriptor(returnTypeDesc));
- }
}
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 568e6fe..c714960 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
@@ -17,7 +17,6 @@
import com.android.tools.r8.keepanno.ast.KeepItemPattern;
import com.android.tools.r8.keepanno.ast.KeepMemberItemPattern;
import com.android.tools.r8.keepanno.ast.KeepMemberPattern;
-import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern.KeepMethodNameExactPattern;
import com.android.tools.r8.keepanno.ast.KeepMethodPattern;
import com.android.tools.r8.keepanno.ast.KeepPreconditions;
import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern;
@@ -203,9 +202,9 @@
}
private void writeMethod(KeepMethodPattern method, AnnotationVisitor targetVisitor) {
- KeepMethodNameExactPattern exactMethodName = method.getNamePattern().asExact();
+ String exactMethodName = method.getNamePattern().asExactString();
if (exactMethodName != null) {
- targetVisitor.visit(Item.methodName, exactMethodName.getName());
+ targetVisitor.visit(Item.methodName, exactMethodName);
} else {
throw new Unimplemented();
}
@@ -213,9 +212,8 @@
throw new Unimplemented();
}
if (!method.getReturnTypePattern().isAny()) {
- if (exactMethodName != null
- && (exactMethodName.getName().equals("<init>")
- || exactMethodName.getName().equals("<clinit>"))
+ if ((method.getNamePattern().isInstanceInitializer()
+ || method.getNamePattern().isClassInitializer())
&& method.getReturnTypePattern().isVoid()) {
// constructors have implicit void return.
} else {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/MethodParametersParser.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/MethodParametersParser.java
new file mode 100644
index 0000000..f1cba56
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/MethodParametersParser.java
@@ -0,0 +1,30 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.keepanno.asm;
+
+import com.android.tools.r8.keepanno.asm.TypeParser.TypeProperty;
+import com.android.tools.r8.keepanno.ast.KeepMethodParametersPattern;
+import com.android.tools.r8.keepanno.ast.KeepTypePattern;
+import com.android.tools.r8.keepanno.ast.ParsingContext;
+import java.util.List;
+
+public class MethodParametersParser
+ extends ConvertingPropertyParser<
+ List<KeepTypePattern>, KeepMethodParametersPattern, TypeProperty> {
+
+ public MethodParametersParser(ParsingContext parsingContext) {
+ super(
+ new ArrayPropertyParser<>(parsingContext, TypeParser::new),
+ MethodParametersParser::convert);
+ }
+
+ private static KeepMethodParametersPattern convert(List<KeepTypePattern> params) {
+ KeepMethodParametersPattern.Builder builder = KeepMethodParametersPattern.builder();
+ for (KeepTypePattern param : params) {
+ builder.addParameterTypePattern(param);
+ }
+ return builder.build();
+ }
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/MethodReturnTypeParser.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/MethodReturnTypeParser.java
new file mode 100644
index 0000000..ccf5183
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/MethodReturnTypeParser.java
@@ -0,0 +1,81 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.keepanno.asm;
+
+import com.android.tools.r8.keepanno.asm.TypeParser.TypeProperty;
+import com.android.tools.r8.keepanno.ast.KeepMethodReturnTypePattern;
+import com.android.tools.r8.keepanno.ast.KeepTypePattern;
+import com.android.tools.r8.keepanno.ast.ParsingContext;
+import java.util.function.Consumer;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Type;
+
+/**
+ * Parser for parsing method return types.
+ *
+ * <p>This parser wraps a type parser and adds support for parsing the string name {@code "void"} or
+ * the class constant {@code void.class} as the void method return type.
+ */
+public class MethodReturnTypeParser
+ extends PropertyParserBase<KeepMethodReturnTypePattern, TypeProperty> {
+
+ private final TypeParser typeParser;
+
+ public MethodReturnTypeParser(ParsingContext parsingContext) {
+ super(parsingContext);
+ typeParser = new TypeParser(parsingContext);
+ }
+
+ static Consumer<KeepTypePattern> wrap(Consumer<KeepMethodReturnTypePattern> fn) {
+ return t -> fn.accept(KeepMethodReturnTypePattern.fromType(t));
+ }
+
+ @Override
+ public KeepMethodReturnTypePattern getValue() {
+ return super.getValue();
+ }
+
+ @Override
+ boolean tryProperty(
+ TypeProperty property,
+ String name,
+ Object value,
+ Consumer<KeepMethodReturnTypePattern> setValue) {
+ if (property == TypeProperty.TYPE_NAME && "void".equals(value)) {
+ setValue.accept(KeepMethodReturnTypePattern.voidType());
+ return true;
+ }
+ if (property == TypeProperty.TYPE_CONSTANT && Type.getType("V").equals(value)) {
+ setValue.accept(KeepMethodReturnTypePattern.voidType());
+ return true;
+ }
+ return typeParser.tryProperty(property, name, value, wrap(setValue));
+ }
+
+ @Override
+ public boolean tryPropertyEnum(
+ TypeProperty property,
+ String name,
+ String descriptor,
+ String value,
+ Consumer<KeepMethodReturnTypePattern> setValue) {
+ return typeParser.tryPropertyEnum(property, name, descriptor, value, wrap(setValue));
+ }
+
+ @Override
+ AnnotationVisitor tryPropertyArray(
+ TypeProperty property, String name, Consumer<KeepMethodReturnTypePattern> setValue) {
+ return typeParser.tryPropertyArray(property, name, wrap(setValue));
+ }
+
+ @Override
+ AnnotationVisitor tryPropertyAnnotation(
+ TypeProperty property,
+ String name,
+ String descriptor,
+ Consumer<KeepMethodReturnTypePattern> setValue) {
+ return typeParser.tryPropertyAnnotation(property, name, descriptor, wrap(setValue));
+ }
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/PackageNameParser.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/PackageNameParser.java
index dadb8de..9c9295d 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/PackageNameParser.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/PackageNameParser.java
@@ -9,8 +9,7 @@
import com.android.tools.r8.keepanno.ast.ParsingContext;
import java.util.function.Consumer;
-public class PackageNameParser
- extends PropertyParserBase<KeepPackagePattern, PackageNameProperty, PackageNameParser> {
+public class PackageNameParser extends PropertyParserBase<KeepPackagePattern, PackageNameProperty> {
public PackageNameParser(ParsingContext parsingContext) {
super(parsingContext);
@@ -21,11 +20,6 @@
}
@Override
- public PackageNameParser self() {
- return this;
- }
-
- @Override
public boolean tryProperty(
PackageNameProperty property,
String name,
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ParserVisitor.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ParserVisitor.java
index 77d6087..5f1f69b 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ParserVisitor.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ParserVisitor.java
@@ -12,13 +12,13 @@
/** Convert parser(s) into an annotation visitor. */
public class ParserVisitor extends AnnotationVisitorBase {
- private final List<PropertyParser<?, ?, ?>> parsers;
+ private final List<PropertyParser<?, ?>> parsers;
private final Runnable onVisitEnd;
public ParserVisitor(
AnnotationParsingContext parsingContext,
String annotationDescriptor,
- List<PropertyParser<?, ?, ?>> parsers,
+ List<PropertyParser<?, ?>> parsers,
Runnable onVisitEnd) {
super(parsingContext);
this.parsers = parsers;
@@ -29,7 +29,7 @@
public ParserVisitor(
AnnotationParsingContext parsingContext,
String annotationDescriptor,
- PropertyParser<?, ?, ?> declaration,
+ PropertyParser<?, ?> declaration,
Runnable onVisitEnd) {
this(parsingContext, annotationDescriptor, Collections.singletonList(declaration), onVisitEnd);
}
@@ -38,7 +38,7 @@
@Override
public void visit(String name, Object value) {
- for (PropertyParser<?, ?, ?> parser : parsers) {
+ for (PropertyParser<?, ?> parser : parsers) {
if (parser.tryParse(name, value, this::ignore)) {
return;
}
@@ -48,7 +48,7 @@
@Override
public AnnotationVisitor visitArray(String name) {
- for (PropertyParser<?, ?, ?> parser : parsers) {
+ for (PropertyParser<?, ?> parser : parsers) {
AnnotationVisitor visitor = parser.tryParseArray(name, this::ignore);
if (visitor != null) {
return visitor;
@@ -59,7 +59,7 @@
@Override
public void visitEnum(String name, String descriptor, String value) {
- for (PropertyParser<?, ?, ?> parser : parsers) {
+ for (PropertyParser<?, ?> parser : parsers) {
if (parser.tryParseEnum(name, descriptor, value, this::ignore)) {
return;
}
@@ -69,7 +69,7 @@
@Override
public AnnotationVisitor visitAnnotation(String name, String descriptor) {
- for (PropertyParser<?, ?, ?> parser : parsers) {
+ for (PropertyParser<?, ?> parser : parsers) {
AnnotationVisitor visitor = parser.tryParseAnnotation(name, descriptor, this::ignore);
if (visitor != null) {
return visitor;
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/PropertyParser.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/PropertyParser.java
index 67b181b..0fd8a75 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/PropertyParser.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/PropertyParser.java
@@ -7,13 +7,9 @@
import java.util.function.Consumer;
import org.objectweb.asm.AnnotationVisitor;
-public interface PropertyParser<T, P, S> {
+public interface PropertyParser<T, P> {
- S self();
-
- String kind();
-
- S setProperty(P property, String name);
+ void setProperty(String name, P property);
boolean isDeclared();
@@ -23,7 +19,16 @@
T getValue();
- boolean tryParse(String name, Object value, Consumer<T> setValue);
+ T tryParse(String name, Object value);
+
+ default boolean tryParse(String name, Object value, Consumer<T> setValue) {
+ T result = tryParse(name, value);
+ if (result != null) {
+ setValue.accept(result);
+ return true;
+ }
+ return false;
+ }
boolean tryParseEnum(String name, String descriptor, String value, Consumer<T> setValue);
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/PropertyParserBase.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/PropertyParserBase.java
index 108f086..1ab1fea 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/PropertyParserBase.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/PropertyParserBase.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.keepanno.asm;
-import com.android.tools.r8.keepanno.ast.KeepEdgeException;
import com.android.tools.r8.keepanno.ast.ParsingContext;
import java.util.HashMap;
import java.util.Map;
@@ -12,11 +11,10 @@
import org.objectweb.asm.AnnotationVisitor;
/** Special case of a property parser allowing only a single value callback. */
-public abstract class PropertyParserBase<T, P, S> implements PropertyParser<T, P, S> {
+public abstract class PropertyParserBase<T, P> implements PropertyParser<T, P> {
private final ParsingContext parsingContext;
- private String kind;
private final Map<String, P> mapping = new HashMap<>();
private String resultPropertyName = null;
private T resultValue = null;
@@ -29,6 +27,10 @@
return parsingContext;
}
+ Map<String, P> getMapping() {
+ return mapping;
+ }
+
boolean tryProperty(P property, String name, Object value, Consumer<T> setValue) {
return false;
}
@@ -62,14 +64,8 @@
}
private void error(String name) {
- throw new KeepEdgeException(
- "Multiple properties defining "
- + kind()
- + ": '"
- + resultPropertyName
- + "' and '"
- + name
- + "'");
+ throw parsingContext.error(
+ "Multiple properties: '" + resultPropertyName + "' and '" + name + "'");
}
public final boolean isDeclared() {
@@ -87,38 +83,26 @@
return isDeclared() ? resultValue : defaultValue;
}
- /** Helper for parsing directly. Returns non-null if the property-name triggered parsing. */
- public final T tryParse(String name, Object value) {
- boolean triggered = tryParse(name, value, unused -> {});
- assert triggered == (resultValue != null);
- return resultValue;
- }
-
- public String kind() {
- return kind != null ? kind : "";
- }
-
- public S setKind(String kind) {
- this.kind = kind;
- return self();
- }
-
/** Add property parsing for the given property-name. */
- public S setProperty(P property, String name) {
+ public void setProperty(String name, P property) {
P old = mapping.put(name, property);
if (old != null) {
throw new IllegalArgumentException("Unexpected attempt to redefine property " + name);
}
- return self();
}
@Override
- public final boolean tryParse(String name, Object value, Consumer<T> setValue) {
+ public final T tryParse(String name, Object value) {
P prop = mapping.get(name);
if (prop != null) {
- return tryProperty(prop, name, value, wrap(name, setValue));
+ try {
+ tryProperty(prop, name, value, wrap(name, unused -> {}));
+ } catch (RuntimeException e) {
+ throw parsingContext.rethrow(e);
+ }
+ return resultValue;
}
- return false;
+ return null;
}
@Override
@@ -126,7 +110,11 @@
String name, String descriptor, String value, Consumer<T> setValue) {
P prop = mapping.get(name);
if (prop != null) {
- return tryPropertyEnum(prop, name, descriptor, value, wrap(name, setValue));
+ try {
+ return tryPropertyEnum(prop, name, descriptor, value, wrap(name, setValue));
+ } catch (RuntimeException e) {
+ throw parsingContext.rethrow(e);
+ }
}
return false;
}
@@ -135,7 +123,11 @@
public final AnnotationVisitor tryParseArray(String name, Consumer<T> setValue) {
P prop = mapping.get(name);
if (prop != null) {
- return tryPropertyArray(prop, name, wrap(name, setValue));
+ try {
+ return tryPropertyArray(prop, name, wrap(name, setValue));
+ } catch (RuntimeException e) {
+ throw parsingContext.rethrow(e);
+ }
}
return null;
}
@@ -145,7 +137,11 @@
String name, String descriptor, Consumer<T> setValue) {
P prop = mapping.get(name);
if (prop != null) {
- return tryPropertyAnnotation(prop, name, descriptor, wrap(name, setValue));
+ try {
+ return tryPropertyAnnotation(prop, name, descriptor, wrap(name, setValue));
+ } catch (RuntimeException e) {
+ throw parsingContext.rethrow(e);
+ }
}
return null;
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/StringPatternParser.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/StringPatternParser.java
new file mode 100644
index 0000000..a7b8a3b
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/StringPatternParser.java
@@ -0,0 +1,105 @@
+// 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.StringPatternParser.StringProperty;
+import com.android.tools.r8.keepanno.ast.AnnotationConstants.StringPattern;
+import com.android.tools.r8.keepanno.ast.KeepStringPattern;
+import com.android.tools.r8.keepanno.ast.ParsingContext;
+import com.android.tools.r8.keepanno.ast.ParsingContext.AnnotationParsingContext;
+import com.google.common.collect.ImmutableList;
+import java.util.function.Consumer;
+import org.objectweb.asm.AnnotationVisitor;
+
+public class StringPatternParser extends PropertyParserBase<KeepStringPattern, StringProperty> {
+
+ public StringPatternParser(ParsingContext parsingContext) {
+ super(parsingContext);
+ }
+
+ public enum StringProperty {
+ EXACT,
+ PATTERN
+ }
+
+ @Override
+ boolean tryProperty(
+ StringProperty property, String name, Object value, Consumer<KeepStringPattern> setValue) {
+ switch (property) {
+ case EXACT:
+ {
+ setValue.accept(KeepStringPattern.exact((String) value));
+ return true;
+ }
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ AnnotationVisitor tryPropertyAnnotation(
+ StringProperty property,
+ String name,
+ String descriptor,
+ Consumer<KeepStringPattern> setValue) {
+ switch (property) {
+ case PATTERN:
+ {
+ AnnotationParsingContext parsingContext =
+ getParsingContext().property(name).annotation(descriptor);
+ StringPatternParser exactParser = new StringPatternParser(parsingContext);
+ StringParser prefixParser = new StringParser(parsingContext);
+ StringParser suffixParser = new StringParser(parsingContext);
+ exactParser.setProperty(StringPattern.exact, StringProperty.EXACT);
+ prefixParser.setProperty(StringPattern.startsWith, StringParser.Property.STRING);
+ suffixParser.setProperty(StringPattern.endsWith, StringParser.Property.STRING);
+ return new ParserVisitor(
+ parsingContext,
+ descriptor,
+ ImmutableList.of(exactParser, prefixParser, suffixParser),
+ () -> {
+ if (exactParser.isDeclared()) {
+ if (prefixParser.isDeclared()) {
+ throw parsingContext.error("Cannot specify both the exact string and a prefix");
+ }
+ if (suffixParser.isDeclared()) {
+ throw parsingContext.error("Cannot specify both the exact string and a suffix");
+ }
+ setValue.accept(exactParser.getValue());
+ } else {
+ setValue.accept(
+ KeepStringPattern.builder()
+ .setPrefix(prefixParser.getValueOrDefault(null))
+ .setSuffix(suffixParser.getValueOrDefault(null))
+ .build());
+ }
+ });
+ }
+ default:
+ return null;
+ }
+ }
+
+ private static class StringParser extends PropertyParserBase<String, StringParser.Property> {
+
+ enum Property {
+ STRING
+ }
+
+ protected StringParser(ParsingContext parsingContext) {
+ super(parsingContext);
+ }
+
+ @Override
+ boolean tryProperty(
+ StringParser.Property property, String name, Object value, Consumer<String> setValue) {
+ assert Property.STRING.equals(property);
+ if (value instanceof String) {
+ setValue.accept((String) value);
+ }
+ return false;
+ }
+ }
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/TypeParser.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/TypeParser.java
index 6b11f70..a40f450 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/TypeParser.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/TypeParser.java
@@ -10,51 +10,31 @@
import com.android.tools.r8.keepanno.ast.KeepTypePattern;
import com.android.tools.r8.keepanno.ast.ParsingContext;
import com.android.tools.r8.keepanno.ast.ParsingContext.AnnotationParsingContext;
-import com.android.tools.r8.keepanno.utils.Unimplemented;
import java.util.function.Consumer;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Type;
-public class TypeParser extends PropertyParserBase<KeepTypePattern, TypeProperty, TypeParser> {
+public class TypeParser extends PropertyParserBase<KeepTypePattern, TypeProperty> {
public TypeParser(ParsingContext parsingContext) {
super(parsingContext);
}
public enum TypeProperty {
- SELF_PATTERN,
+ TYPE_PATTERN,
TYPE_NAME,
TYPE_CONSTANT,
CLASS_NAME_PATTERN
}
- public TypeParser enableTypePattern(String propertyName) {
- return setProperty(TypeProperty.SELF_PATTERN, propertyName);
- }
-
- public TypeParser enableTypeName(String propertyName) {
- return setProperty(TypeProperty.TYPE_NAME, propertyName);
- }
-
- public TypeParser enableTypeConstant(String propertyName) {
- return setProperty(TypeProperty.TYPE_CONSTANT, propertyName);
- }
-
- public TypeParser enableTypeClassNamePattern(String propertyName) {
- return setProperty(TypeProperty.CLASS_NAME_PATTERN, propertyName);
- }
-
- @Override
- public TypeParser self() {
- return this;
- }
-
@Override
public boolean tryProperty(
TypeProperty property, String name, Object value, Consumer<KeepTypePattern> setValue) {
switch (property) {
case TYPE_NAME:
- setValue.accept(KeepEdgeReaderUtils.typePatternFromString((String) value));
+ setValue.accept(
+ KeepEdgeReaderUtils.typePatternFromString(
+ (String) value, getParsingContext().property(name)));
return true;
case TYPE_CONSTANT:
setValue.accept(KeepTypePattern.fromDescriptor(((Type) value).getDescriptor()));
@@ -68,39 +48,28 @@
public AnnotationVisitor tryPropertyAnnotation(
TypeProperty property, String name, String descriptor, Consumer<KeepTypePattern> setValue) {
switch (property) {
- case SELF_PATTERN:
+ case TYPE_PATTERN:
{
- AnnotationParsingContext parsingContext =
- new AnnotationParsingContext(getParsingContext(), descriptor);
- TypeParser typeParser =
- new TypeParser(parsingContext)
- .setKind(kind())
- .enableTypeName(TypePattern.name)
- .enableTypeConstant(TypePattern.constant)
- .enableTypeClassNamePattern(TypePattern.classNamePattern);
+ AnnotationParsingContext context =
+ getParsingContext().property(name).annotation(descriptor);
+ TypeParser typeParser = new TypeParser(context);
+ typeParser.setProperty(TypePattern.name, TypeProperty.TYPE_NAME);
+ typeParser.setProperty(TypePattern.constant, TypeProperty.TYPE_CONSTANT);
+ typeParser.setProperty(TypePattern.classNamePattern, TypeProperty.CLASS_NAME_PATTERN);
return new ParserVisitor(
- parsingContext,
+ context,
descriptor,
typeParser,
() -> setValue.accept(typeParser.getValueOrDefault(KeepTypePattern.any())));
}
case CLASS_NAME_PATTERN:
{
- return new ClassNameParser(getParsingContext())
- .setKind(kind())
- .tryPropertyAnnotation(
- ClassNameProperty.PATTERN,
- name,
- descriptor,
- classNamePattern -> {
- if (classNamePattern.isExact()) {
- setValue.accept(
- KeepTypePattern.fromDescriptor(classNamePattern.getExactDescriptor()));
- } else {
- // TODO(b/248408342): Extend the AST type patterns.
- throw new Unimplemented("Non-exact class patterns are not implemented yet");
- }
- });
+ ClassNameParser parser = new ClassNameParser(getParsingContext());
+ return parser.tryPropertyAnnotation(
+ ClassNameProperty.PATTERN,
+ name,
+ descriptor,
+ value -> setValue.accept(KeepTypePattern.fromClass(value)));
}
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 13a228b..c489701 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
@@ -65,11 +65,15 @@
/** Item properties common to binding items, conditions and targets. */
public static final class Item {
+ public static final String classGroup = "class";
public static final String classFromBinding = "classFromBinding";
+ public static final String memberGroup = "member";
public static final String memberFromBinding = "memberFromBinding";
+ public static final String classNameGroup = "class-name";
public static final String className = "className";
public static final String classConstant = "classConstant";
public static final String classNamePattern = "classNamePattern";
+ public static final String instanceOfGroup = "instance-of";
public static final String instanceOfClassName = "instanceOfClassName";
public static final String instanceOfClassNameExclusive = "instanceOfClassNameExclusive";
public static final String instanceOfClassConstant = "instanceOfClassConstant";
@@ -79,14 +83,19 @@
public static final String extendsClassConstant = "extendsClassConstant";
public static final String memberAccess = "memberAccess";
public static final String methodAccess = "methodAccess";
+ public static final String methodNameGroup = "method-name";
public static final String methodName = "methodName";
+ public static final String methodNamePattern = "methodNamePattern";
+ public static final String returnTypeGroup = "return-type";
public static final String methodReturnType = "methodReturnType";
public static final String methodReturnTypeConstant = "methodReturnTypeConstant";
public static final String methodReturnTypePattern = "methodReturnTypePattern";
+ public static final String parametersGroup = "parameters";
public static final String methodParameters = "methodParameters";
public static final String methodParameterTypePatterns = "methodParameterTypePatterns";
public static final String fieldAccess = "fieldAccess";
public static final String fieldName = "fieldName";
+ public static final String fieldTypeGroup = "field-type";
public static final String fieldType = "fieldType";
public static final String fieldTypeConstant = "fieldTypeConstant";
public static final String fieldTypePattern = "fieldTypePattern";
@@ -107,6 +116,7 @@
public static final String DESCRIPTOR =
"Lcom/android/tools/r8/keepanno/annotations/KeepTarget;";
public static final String kind = "kind";
+ public static final String constraintsGroup = "constraints";
public static final String constraints = "constraints";
public static final String allow = "allow";
public static final String disallow = "disallow";
@@ -182,9 +192,19 @@
public static final String TRANSIENT = "TRANSIENT";
}
+ public static final class StringPattern {
+ public static final String DESCRIPTOR =
+ "Lcom/android/tools/r8/keepanno/annotations/StringPattern;";
+ public static final String stringExactPatternGroup = "string-exact-pattern";
+ public static final String exact = "exact";
+ public static final String startsWith = "startsWith";
+ public static final String endsWith = "endsWith";
+ }
+
public static final class TypePattern {
public static final String DESCRIPTOR =
"Lcom/android/tools/r8/keepanno/annotations/TypePattern;";
+ public static final String typePatternGroup = "type-pattern";
public static final String name = "name";
public static final String constant = "constant";
public static final String classNamePattern = "classNamePattern";
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepAnnotationParserException.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepAnnotationParserException.java
index d98d410..7a08a6a 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepAnnotationParserException.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepAnnotationParserException.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.keepanno.ast;
-public class KeepAnnotationParserException extends KeepEdgeException {
+public class KeepAnnotationParserException extends RuntimeException {
private final ParsingContext context;
@@ -13,6 +13,11 @@
this.context = context;
}
+ public KeepAnnotationParserException(ParsingContext context, RuntimeException cause) {
+ super(cause);
+ this.context = context;
+ }
+
@Override
public String getMessage() {
return super.getMessage() + getContextAsString();
@@ -22,7 +27,11 @@
StringBuilder builder = new StringBuilder();
ParsingContext current = context;
while (current != null) {
- builder.append("\n in ").append(current.getContextFrameAsString());
+ builder
+ .append("\n at ")
+ .append(current.getContextType())
+ .append(": ")
+ .append(current.getContextFrameAsString());
current = current.getParentContext();
}
return builder.toString();
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepArrayTypePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepArrayTypePattern.java
new file mode 100644
index 0000000..96bdd8a
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepArrayTypePattern.java
@@ -0,0 +1,56 @@
+// 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 java.util.Objects;
+
+public class KeepArrayTypePattern {
+
+ private static final KeepArrayTypePattern ANY =
+ new KeepArrayTypePattern(KeepTypePattern.any(), 1);
+
+ public static KeepArrayTypePattern getAny() {
+ return ANY;
+ }
+
+ private final KeepTypePattern baseType;
+ private final int dimensions;
+
+ public KeepArrayTypePattern(KeepTypePattern baseType, int dimensions) {
+ assert baseType != null;
+ assert dimensions > 0;
+ this.baseType = baseType;
+ this.dimensions = dimensions;
+ }
+
+ public boolean isAny() {
+ return ANY.equals(this);
+ }
+
+ public KeepTypePattern getBaseType() {
+ return baseType;
+ }
+
+ public int getDimensions() {
+ return dimensions;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof KeepArrayTypePattern)) {
+ return false;
+ }
+ KeepArrayTypePattern that = (KeepArrayTypePattern) o;
+ return dimensions == that.dimensions && Objects.equals(baseType, that.baseType);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(baseType, dimensions);
+ }
+}
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 34dcab5..04485d7 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
@@ -44,7 +44,15 @@
* CLASS_ITEM_PATTERN ::= class QUALIFIED_CLASS_NAME_PATTERN instance-of INSTANCE_OF_PATTERN
* MEMBER_ITEM_PATTERN ::= CLASS_ITEM_REFERENCE { MEMBER_PATTERN }
*
- * TYPE_PATTERN ::= any | exact type-descriptor
+ * TYPE_PATTERN
+ * ::= any
+ * | PRIMITIVE_TYPE_PATTERN
+ * | ARRAY_TYPE_PATTERN
+ * | QUALIFIED_CLASS_NAME_PATTERN
+ *
+ * PRIMITIVE_TYPE_PATTERN ::= any | boolean | byte | char | short | int | long | float | double
+ * ARRAY_TYPE_PATTERN ::= any | TYPE_PATTERN dimensions(N > 0)
+ *
* PACKAGE_PATTERN ::= any | exact package-name
*
* QUALIFIED_CLASS_NAME_PATTERN
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodNamePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodNamePattern.java
index 18a399a..6843e61 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodNamePattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodNamePattern.java
@@ -5,99 +5,115 @@
public abstract class KeepMethodNamePattern {
+ private static final String INIT_STRING = "<init>";
+ private static final String CLINIT_STRING = "<clinit>";
public static KeepMethodNamePattern any() {
- return Any.getInstance();
+ return SomePattern.ANY;
}
public static KeepMethodNamePattern initializer() {
- return new KeepMethodNameExactPattern("<init>");
+ return SomePattern.INSTANCE_INIT;
}
public static KeepMethodNamePattern exact(String methodName) {
- return new KeepMethodNameExactPattern(methodName);
+ return fromStringPattern(KeepStringPattern.exact(methodName));
+ }
+
+ public static KeepMethodNamePattern fromStringPattern(KeepStringPattern pattern) {
+ if (pattern.isAny()) {
+ return SomePattern.ANY;
+ }
+ if (pattern.isExact()) {
+ String exact = pattern.asExactString();
+ if (INIT_STRING.equals(exact)) {
+ return SomePattern.INSTANCE_INIT;
+ }
+ if (CLINIT_STRING.equals(exact)) {
+ return SomePattern.CLASS_INIT;
+ }
+ }
+ return new SomePattern(pattern);
}
private KeepMethodNamePattern() {}
- public boolean isAny() {
- return false;
- }
+ public abstract boolean isAny();
- public final boolean isExact() {
- return asExact() != null;
- }
+ public abstract boolean isInstanceInitializer();
- public KeepMethodNameExactPattern asExact() {
- return null;
- }
+ public abstract boolean isClassInitializer();
- private static class Any extends KeepMethodNamePattern {
- private static final Any INSTANCE = new Any();
+ public abstract boolean isExact();
- public static Any getInstance() {
- return INSTANCE;
+ public abstract String asExactString();
+
+ public abstract KeepStringPattern asStringPattern();
+
+ private static class SomePattern extends KeepMethodNamePattern {
+ private static final SomePattern ANY = new SomePattern(KeepStringPattern.any());
+ private static final KeepMethodNamePattern INSTANCE_INIT =
+ new SomePattern(KeepStringPattern.exact("<init>"));
+ private static final KeepMethodNamePattern CLASS_INIT =
+ new SomePattern(KeepStringPattern.exact("<clinit>"));
+
+ private final KeepStringPattern pattern;
+
+ public SomePattern(KeepStringPattern pattern) {
+ assert pattern != null;
+ this.pattern = pattern;
}
@Override
- public boolean isAny() {
- return true;
+ public KeepStringPattern asStringPattern() {
+ return pattern;
}
@Override
- public boolean equals(Object obj) {
- return this == obj;
- }
-
- @Override
- public int hashCode() {
- return System.identityHashCode(this);
- }
-
- @Override
- public String toString() {
- return "*";
- }
- }
-
- public static class KeepMethodNameExactPattern extends KeepMethodNamePattern {
- private final String name;
-
- public KeepMethodNameExactPattern(String name) {
- assert name != null;
- this.name = name;
- }
-
- @Override
- public KeepMethodNameExactPattern asExact() {
- return this;
- }
-
- public String getName() {
- return name;
- }
-
- @Override
- @SuppressWarnings("EqualsGetClass")
public boolean equals(Object o) {
if (this == o) {
return true;
}
- if (o == null || getClass() != o.getClass()) {
+ if (!(o instanceof SomePattern)) {
return false;
}
- KeepMethodNameExactPattern that = (KeepMethodNameExactPattern) o;
- return name.equals(that.name);
+ SomePattern that = (SomePattern) o;
+ return pattern.equals(that.pattern);
}
@Override
public int hashCode() {
- return name.hashCode();
+ return pattern.hashCode();
}
@Override
public String toString() {
- return name;
+ return pattern.toString();
+ }
+
+ @Override
+ public boolean isAny() {
+ return ANY == this;
+ }
+
+ @Override
+ public boolean isClassInitializer() {
+ return CLASS_INIT == this;
+ }
+
+ @Override
+ public boolean isInstanceInitializer() {
+ return INSTANCE_INIT == this;
+ }
+
+ @Override
+ public boolean isExact() {
+ return pattern.isExact();
+ }
+
+ @Override
+ public String asExactString() {
+ return pattern.asExactString();
}
}
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java
index a7a4977..98ebbb9 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java
@@ -3,7 +3,6 @@
// 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.KeepMethodNamePattern.KeepMethodNameExactPattern;
import java.util.Objects;
public final class KeepMethodPattern extends KeepMemberPattern {
@@ -55,9 +54,7 @@
public KeepMethodPattern build() {
KeepMethodReturnTypePattern returnTypePattern = this.returnTypePattern;
- KeepMethodNameExactPattern exactName = namePattern.asExact();
- if (exactName != null
- && (exactName.getName().equals("<init>") || exactName.getName().equals("<clinit>"))) {
+ if (namePattern.isInstanceInitializer() || namePattern.isClassInitializer()) {
if (!this.returnTypePattern.isAny() && !this.returnTypePattern.isVoid()) {
throw new KeepEdgeException("Method constructor pattern must match 'void' type.");
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepPrimitiveTypePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepPrimitiveTypePattern.java
new file mode 100644
index 0000000..f79093d
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepPrimitiveTypePattern.java
@@ -0,0 +1,95 @@
+// 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.ImmutableMap;
+import java.util.Map;
+import java.util.function.Consumer;
+
+public class KeepPrimitiveTypePattern {
+
+ private static final KeepPrimitiveTypePattern ANY = new KeepPrimitiveTypePattern('*');
+ private static final KeepPrimitiveTypePattern BOOLEAN = new KeepPrimitiveTypePattern('Z');
+ private static final KeepPrimitiveTypePattern BYTE = new KeepPrimitiveTypePattern('B');
+ private static final KeepPrimitiveTypePattern CHAR = new KeepPrimitiveTypePattern('C');
+ private static final KeepPrimitiveTypePattern SHORT = new KeepPrimitiveTypePattern('S');
+ private static final KeepPrimitiveTypePattern INT = new KeepPrimitiveTypePattern('I');
+ private static final KeepPrimitiveTypePattern LONG = new KeepPrimitiveTypePattern('J');
+ private static final KeepPrimitiveTypePattern FLOAT = new KeepPrimitiveTypePattern('F');
+ private static final KeepPrimitiveTypePattern DOUBLE = new KeepPrimitiveTypePattern('D');
+
+ private static final Map<String, KeepPrimitiveTypePattern> PRIMITIVES =
+ populate(BOOLEAN, BYTE, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE);
+
+ private static ImmutableMap<String, KeepPrimitiveTypePattern> populate(
+ KeepPrimitiveTypePattern... types) {
+ ImmutableMap.Builder<String, KeepPrimitiveTypePattern> builder = ImmutableMap.builder();
+ for (KeepPrimitiveTypePattern type : types) {
+ builder.put(type.getDescriptor(), type);
+ }
+ return builder.build();
+ }
+
+ public static KeepPrimitiveTypePattern getAny() {
+ return ANY;
+ }
+
+ public static KeepPrimitiveTypePattern getBoolean() {
+ return BOOLEAN;
+ }
+
+ public static KeepPrimitiveTypePattern getByte() {
+ return BYTE;
+ }
+
+ public static KeepPrimitiveTypePattern getChar() {
+ return CHAR;
+ }
+
+ public static KeepPrimitiveTypePattern getShort() {
+ return SHORT;
+ }
+
+ public static KeepPrimitiveTypePattern getInt() {
+ return INT;
+ }
+
+ public static KeepPrimitiveTypePattern getLong() {
+ return LONG;
+ }
+
+ public static KeepPrimitiveTypePattern getFloat() {
+ return FLOAT;
+ }
+
+ public static KeepPrimitiveTypePattern getDouble() {
+ return DOUBLE;
+ }
+
+ private final char descriptor;
+
+ public KeepPrimitiveTypePattern(char descriptor) {
+ this.descriptor = descriptor;
+ }
+
+ public boolean isAny() {
+ return this == ANY;
+ }
+
+ public char getDescriptorChar() {
+ if (isAny()) {
+ throw new KeepEdgeException("No descriptor exists for 'any' primitive");
+ }
+ return descriptor;
+ }
+
+ public String getDescriptor() {
+ return Character.toString(getDescriptorChar());
+ }
+
+ public static void forEachPrimitive(Consumer<KeepPrimitiveTypePattern> fn) {
+ PRIMITIVES.values().forEach(fn);
+ }
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepQualifiedClassNamePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepQualifiedClassNamePattern.java
index ee7f3d9..574336d 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepQualifiedClassNamePattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepQualifiedClassNamePattern.java
@@ -18,6 +18,13 @@
.build();
}
+ public static KeepQualifiedClassNamePattern exactFromDescriptor(String classDescriptor) {
+ if (!classDescriptor.startsWith("L") && classDescriptor.endsWith(";")) {
+ throw new KeepEdgeException("Invalid class descriptor: " + classDescriptor);
+ }
+ return exact(classDescriptor.substring(1, classDescriptor.length() - 1).replace('/', '.'));
+ }
+
public static KeepQualifiedClassNamePattern exact(String qualifiedClassName) {
int pkgSeparator = qualifiedClassName.lastIndexOf('.');
if (pkgSeparator == 0) {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepStringPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepStringPattern.java
new file mode 100644
index 0000000..8d36ea9
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepStringPattern.java
@@ -0,0 +1,93 @@
+// 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;
+
+public class KeepStringPattern {
+
+ private static final KeepStringPattern ANY = new KeepStringPattern(null, null, null);
+
+ public static KeepStringPattern any() {
+ return ANY;
+ }
+
+ public static KeepStringPattern exact(String exact) {
+ return KeepStringPattern.builder().setExact(exact).build();
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ private String exact = null;
+ private String prefix = null;
+ private String suffix = null;
+
+ private Builder() {}
+
+ public Builder setExact(String exact) {
+ this.exact = exact;
+ return this;
+ }
+
+ public Builder setPrefix(String prefix) {
+ this.prefix = prefix;
+ return this;
+ }
+
+ public Builder setSuffix(String suffix) {
+ this.suffix = suffix;
+ return this;
+ }
+
+ public KeepStringPattern build() {
+ if (exact != null) {
+ return new KeepStringPattern(exact, null, null);
+ }
+ if (prefix == null && suffix == null) {
+ return ANY;
+ }
+ return new KeepStringPattern(exact, prefix, suffix);
+ }
+ }
+
+ private final String exact;
+ private final String prefix;
+ private final String suffix;
+
+ private KeepStringPattern(String exact, String prefix, String suffix) {
+ this.exact = exact;
+ this.prefix = prefix;
+ this.suffix = suffix;
+ }
+
+ public boolean isAny() {
+ return ANY == this;
+ }
+
+ public boolean isExact() {
+ return exact != null;
+ }
+
+ public String asExactString() {
+ return exact;
+ }
+
+ public boolean hasPrefix() {
+ return prefix != null;
+ }
+
+ public boolean hasSuffix() {
+ return suffix != null;
+ }
+
+ public String getPrefixString() {
+ return prefix;
+ }
+
+ public String getSuffixString() {
+ return suffix;
+ }
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTypePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTypePattern.java
index dd56be4..b7c2b0d 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTypePattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTypePattern.java
@@ -3,16 +3,60 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.keepanno.ast;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
public abstract class KeepTypePattern {
public static KeepTypePattern any() {
return Any.getInstance();
}
- public static KeepTypePattern fromDescriptor(String typeDescriptor) {
- return new Some(typeDescriptor);
+ public static KeepTypePattern fromPrimitive(KeepPrimitiveTypePattern type) {
+ return type.isAny() ? PrimitiveType.ANY : PrimitiveType.PRIMITIVES.get(type.getDescriptor());
}
+ public static KeepTypePattern fromArray(KeepArrayTypePattern type) {
+ return new ArrayType(type);
+ }
+
+ public static KeepTypePattern fromClass(KeepQualifiedClassNamePattern type) {
+ return new ClassType(type);
+ }
+
+ public static KeepTypePattern fromDescriptor(String typeDescriptor) {
+ char c = typeDescriptor.charAt(0);
+ if (c == 'L') {
+ int end = typeDescriptor.length() - 1;
+ if (typeDescriptor.charAt(end) != ';') {
+ throw new KeepEdgeException("Invalid type descriptor: " + typeDescriptor);
+ }
+ return fromClass(KeepQualifiedClassNamePattern.exactFromDescriptor(typeDescriptor));
+ }
+ if (c == '[') {
+ int dim = 1;
+ while (typeDescriptor.charAt(dim) == '[') {
+ dim++;
+ }
+ KeepTypePattern baseType = fromDescriptor(typeDescriptor.substring(dim));
+ return fromArray(new KeepArrayTypePattern(baseType, dim));
+ }
+ PrimitiveType primitiveType = PrimitiveType.PRIMITIVES.get(typeDescriptor);
+ if (primitiveType != null) {
+ return primitiveType;
+ }
+ throw new KeepEdgeException("Invalid type descriptor: " + typeDescriptor);
+ }
+
+ public abstract <T> T match(
+ Supplier<T> onAny,
+ Function<KeepPrimitiveTypePattern, T> onPrimitive,
+ Function<KeepArrayTypePattern, T> onArray,
+ Function<KeepQualifiedClassNamePattern, T> onClass);
+
public boolean isAny() {
return false;
}
@@ -21,44 +65,6 @@
return null;
}
- private static class Some extends KeepTypePattern {
-
- private final String descriptor;
-
- private Some(String descriptor) {
- assert descriptor != null;
- this.descriptor = descriptor;
- }
-
- @Override
- public String getDescriptor() {
- return descriptor;
- }
-
- @Override
- @SuppressWarnings("EqualsGetClass")
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- Some some = (Some) o;
- return descriptor.equals(some.descriptor);
- }
-
- @Override
- public int hashCode() {
- return descriptor.hashCode();
- }
-
- @Override
- public String toString() {
- return descriptor;
- }
- }
-
private static class Any extends KeepTypePattern {
private static final Any INSTANCE = new Any();
@@ -68,6 +74,15 @@
}
@Override
+ public <T> T match(
+ Supplier<T> onAny,
+ Function<KeepPrimitiveTypePattern, T> onPrimitive,
+ Function<KeepArrayTypePattern, T> onArray,
+ Function<KeepQualifiedClassNamePattern, T> onClass) {
+ return onAny.get();
+ }
+
+ @Override
public boolean isAny() {
return true;
}
@@ -87,4 +102,117 @@
return "<any>";
}
}
+
+ private static class PrimitiveType extends KeepTypePattern {
+
+ private static final PrimitiveType ANY = new PrimitiveType(KeepPrimitiveTypePattern.getAny());
+ private static final Map<String, PrimitiveType> PRIMITIVES = populate();
+
+ private static Map<String, PrimitiveType> populate() {
+ ImmutableMap.Builder<String, PrimitiveType> builder = ImmutableMap.builder();
+ KeepPrimitiveTypePattern.forEachPrimitive(
+ primitive -> {
+ builder.put(primitive.getDescriptor(), new PrimitiveType(primitive));
+ });
+ return builder.build();
+ }
+
+ private final KeepPrimitiveTypePattern type;
+
+ private PrimitiveType(KeepPrimitiveTypePattern type) {
+ this.type = type;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return this == obj;
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(this);
+ }
+
+ @Override
+ public String toString() {
+ return getDescriptor();
+ }
+
+ @Override
+ public <T> T match(
+ Supplier<T> onAny,
+ Function<KeepPrimitiveTypePattern, T> onPrimitive,
+ Function<KeepArrayTypePattern, T> onArray,
+ Function<KeepQualifiedClassNamePattern, T> onClass) {
+ return onPrimitive.apply(type);
+ }
+ }
+
+ private static class ClassType extends KeepTypePattern {
+ private final KeepQualifiedClassNamePattern type;
+
+ public ClassType(KeepQualifiedClassNamePattern type) {
+ this.type = type;
+ }
+
+ @Override
+ public <T> T match(
+ Supplier<T> onAny,
+ Function<KeepPrimitiveTypePattern, T> onPrimitive,
+ Function<KeepArrayTypePattern, T> onArray,
+ Function<KeepQualifiedClassNamePattern, T> onClass) {
+ return onClass.apply(type);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof ClassType)) {
+ return false;
+ }
+ ClassType classType = (ClassType) o;
+ return Objects.equals(type, classType.type);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type);
+ }
+ }
+
+ private static class ArrayType extends KeepTypePattern {
+ private final KeepArrayTypePattern type;
+
+ public ArrayType(KeepArrayTypePattern type) {
+ this.type = type;
+ }
+
+ @Override
+ public <T> T match(
+ Supplier<T> onAny,
+ Function<KeepPrimitiveTypePattern, T> onPrimitive,
+ Function<KeepArrayTypePattern, T> onArray,
+ Function<KeepQualifiedClassNamePattern, T> onClass) {
+ return onArray.apply(type);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof ArrayType)) {
+ return false;
+ }
+ ArrayType arrayType = (ArrayType) o;
+ return Objects.equals(type, arrayType.type);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type);
+ }
+ }
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/ParsingContext.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/ParsingContext.java
index d7ce9a4..0d83a25 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/ParsingContext.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/ParsingContext.java
@@ -14,14 +14,50 @@
throw new KeepAnnotationParserException(this, message);
}
+ public KeepAnnotationParserException rethrow(RuntimeException e) {
+ if (e instanceof KeepAnnotationParserException) {
+ throw e;
+ }
+ throw new KeepAnnotationParserException(this, e);
+ }
+
public abstract String getHolderName();
public ParsingContext getParentContext() {
return null;
}
+ public abstract String getContextType();
+
public abstract String getContextFrameAsString();
+ public boolean isSynthetic() {
+ return false;
+ }
+
+ private ParsingContext nonSyntheticParent() {
+ // We don't want to maintain nested property groups as they are "synthetic" and only the
+ // inner-most group is useful when diagnosing an error.
+ if (isSynthetic()) {
+ ParsingContext parent = getParentContext();
+ assert !parent.isSynthetic();
+ return parent;
+ }
+ return this;
+ }
+
+ public GroupParsingContext group(String propertyGroupDescription) {
+ return new GroupParsingContext(this, propertyGroupDescription);
+ }
+
+ public AnnotationParsingContext annotation(String annotationDescriptor) {
+ return new AnnotationParsingContext(this, annotationDescriptor);
+ }
+
+ public PropertyParsingContext property(String propertyName) {
+ return new PropertyParsingContext(this, propertyName);
+ }
+
public static class ClassParsingContext extends ParsingContext {
private final String className;
@@ -35,6 +71,11 @@
}
@Override
+ public String getContextType() {
+ return "class";
+ }
+
+ @Override
public String getContextFrameAsString() {
return className;
}
@@ -71,11 +112,15 @@
}
@Override
+ public String getContextType() {
+ return "method";
+ }
+
+ @Override
public String getContextFrameAsString() {
Type methodType = Type.getMethodType(methodDescriptor);
StringBuilder builder = new StringBuilder();
builder
- .append("method ")
.append(getJavaTypeFromDescriptor(methodType.getReturnType().getDescriptor()))
.append(' ')
.append(methodName)
@@ -106,8 +151,13 @@
}
@Override
+ public String getContextType() {
+ return "field";
+ }
+
+ @Override
public String getContextFrameAsString() {
- return "field " + getJavaTypeFromDescriptor(fieldDescriptor) + " " + fieldName;
+ return getJavaTypeFromDescriptor(fieldDescriptor) + " " + fieldName;
}
}
@@ -116,7 +166,7 @@
private final String annotationDescriptor;
public AnnotationParsingContext(ParsingContext parentContext, String annotationDescriptor) {
- this.parentContext = parentContext;
+ this.parentContext = parentContext.nonSyntheticParent();
this.annotationDescriptor = annotationDescriptor;
}
@@ -140,8 +190,88 @@
}
@Override
+ public String getContextType() {
+ return "annotation";
+ }
+
+ @Override
public String getContextFrameAsString() {
return "@" + getSimpleAnnotationName();
}
}
+
+ public static class GroupParsingContext extends ParsingContext {
+ private final ParsingContext parentContext;
+ private final String propertyGroupDescription;
+
+ public GroupParsingContext(ParsingContext parentContext, String propertyGroupDescription) {
+ this.parentContext = parentContext.nonSyntheticParent();
+ this.propertyGroupDescription = propertyGroupDescription;
+ }
+
+ public String getPropertyGroupDescription() {
+ return propertyGroupDescription;
+ }
+
+ @Override
+ public boolean isSynthetic() {
+ // The property "groups" are not actual source info and should only be used in top-level
+ // reporting.
+ return true;
+ }
+
+ @Override
+ public String getHolderName() {
+ return parentContext.getHolderName();
+ }
+
+ @Override
+ public ParsingContext getParentContext() {
+ return parentContext;
+ }
+
+ @Override
+ public String getContextType() {
+ return "property-group";
+ }
+
+ @Override
+ public String getContextFrameAsString() {
+ return getPropertyGroupDescription();
+ }
+ }
+
+ public static class PropertyParsingContext extends ParsingContext {
+ private final ParsingContext parentContext;
+ private final String propertyName;
+
+ public PropertyParsingContext(ParsingContext parentContext, String propertyName) {
+ this.parentContext = parentContext.nonSyntheticParent();
+ this.propertyName = propertyName;
+ }
+
+ public String getPropertyName() {
+ return propertyName;
+ }
+
+ @Override
+ public String getHolderName() {
+ return parentContext.getHolderName();
+ }
+
+ @Override
+ public ParsingContext getParentContext() {
+ return parentContext;
+ }
+
+ @Override
+ public String getContextType() {
+ return "property";
+ }
+
+ @Override
+ public String getContextFrameAsString() {
+ return getPropertyName();
+ }
+ }
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrinter.java b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrinter.java
index 94694a2..518eeba 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrinter.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrinter.java
@@ -25,6 +25,7 @@
public RulePrinter append(String str) {
assert !str.contains("*");
assert !str.contains("(...)");
+ assert !str.contains("%");
return appendWithoutBackReferenceAssert(str);
}
@@ -45,6 +46,10 @@
return appendWithoutBackReferenceAssert("***");
}
+ public RulePrinter appendPercent() {
+ return appendWithoutBackReferenceAssert("%");
+ }
+
public RulePrinter appendAnyParameters() {
return appendWithoutBackReferenceAssert("(...)");
}
@@ -91,6 +96,11 @@
}
@Override
+ public RulePrinter appendPercent() {
+ return addBackRef("%");
+ }
+
+ @Override
public RulePrinter appendAnyParameters() {
// TODO(b/265892343): R8 does not yet support back reference to `...`.
return appendWithoutBackReferenceAssert("(...)");
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrintingUtils.java b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrintingUtils.java
index 5d5c357..f16d9dd 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrintingUtils.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrintingUtils.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.keepanno.keeprules;
import com.android.tools.r8.keepanno.ast.AccessVisibility;
+import com.android.tools.r8.keepanno.ast.KeepArrayTypePattern;
import com.android.tools.r8.keepanno.ast.KeepClassItemPattern;
import com.android.tools.r8.keepanno.ast.KeepEdgeException;
import com.android.tools.r8.keepanno.ast.KeepEdgeMetaInfo;
@@ -21,7 +22,9 @@
import com.android.tools.r8.keepanno.ast.KeepOptions;
import com.android.tools.r8.keepanno.ast.KeepOptions.KeepOption;
import com.android.tools.r8.keepanno.ast.KeepPackagePattern;
+import com.android.tools.r8.keepanno.ast.KeepPrimitiveTypePattern;
import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern;
+import com.android.tools.r8.keepanno.ast.KeepStringPattern;
import com.android.tools.r8.keepanno.ast.KeepTypePattern;
import com.android.tools.r8.keepanno.ast.KeepUnqualfiedClassNamePattern;
import com.android.tools.r8.keepanno.ast.ModifierPattern;
@@ -158,6 +161,20 @@
return builder.append(")");
}
+ private static RulePrinter printStringPattern(RulePrinter printer, KeepStringPattern pattern) {
+ if (pattern.isExact()) {
+ return printer.append(pattern.asExactString());
+ }
+ if (pattern.hasPrefix()) {
+ printer.append(pattern.getPrefixString());
+ }
+ printer.appendStar();
+ if (pattern.hasSuffix()) {
+ printer.append(pattern.getSuffixString());
+ }
+ return printer;
+ }
+
private static RulePrinter printFieldName(RulePrinter builder, KeepFieldNamePattern namePattern) {
return namePattern.isAny()
? builder.appendStar()
@@ -166,9 +183,7 @@
private static RulePrinter printMethodName(
RulePrinter builder, KeepMethodNamePattern namePattern) {
- return namePattern.isAny()
- ? builder.appendStar()
- : builder.append(namePattern.asExact().getName());
+ return printStringPattern(builder, namePattern.asStringPattern());
}
private static RulePrinter printReturnType(
@@ -179,11 +194,34 @@
return printType(builder, returnTypePattern.asType());
}
- private static RulePrinter printType(RulePrinter builder, KeepTypePattern typePattern) {
- if (typePattern.isAny()) {
- return builder.appendTripleStar();
+ private static RulePrinter printType(RulePrinter printer, KeepTypePattern typePattern) {
+ return typePattern.match(
+ printer::appendTripleStar,
+ primitivePattern -> printPrimitiveType(printer, primitivePattern),
+ arrayTypePattern -> printArrayType(printer, arrayTypePattern),
+ classTypePattern -> printClassName(classTypePattern, printer));
+ }
+
+ private static RulePrinter printPrimitiveType(
+ RulePrinter printer, KeepPrimitiveTypePattern primitiveTypePattern) {
+ if (primitiveTypePattern.isAny()) {
+ // Matching any primitive type uses the wildcard syntax `%`
+ return printer.appendPercent();
}
- return builder.append(descriptorToJavaType(typePattern.getDescriptor()));
+ return printer.append(descriptorToJavaType(primitiveTypePattern.getDescriptor()));
+ }
+
+ private static RulePrinter printArrayType(
+ RulePrinter printer, KeepArrayTypePattern arrayTypePattern) {
+ // The "any" array is simply dimension one of any type. Just assert that to be true as the
+ // general case will emit the correct syntax: ***[]
+ assert !arrayTypePattern.isAny()
+ || (arrayTypePattern.getDimensions() == 1 && arrayTypePattern.getBaseType().isAny());
+ printType(printer, arrayTypePattern.getBaseType());
+ for (int i = 0; i < arrayTypePattern.getDimensions(); i++) {
+ printer.append("[]");
+ }
+ return printer;
}
public static RulePrinter printMemberAccess(
@@ -254,7 +292,7 @@
public static RulePrinter printClassName(
KeepQualifiedClassNamePattern classNamePattern, RulePrinter printer) {
if (classNamePattern.isAny()) {
- return printer.appendStar();
+ return printer.appendDoubleStar();
}
printPackagePrefix(classNamePattern.getPackagePattern(), printer);
return printSimpleClassName(classNamePattern.getNamePattern(), printer);
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 2250d81..33b687b 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -755,7 +755,6 @@
// Assert some of R8 optimizations are disabled.
assert !internal.inlinerOptions().enableInlining;
assert !internal.enableClassInlining;
- assert internal.getVerticalClassMergerOptions().isDisabled();
assert !internal.enableEnumValueOptimization;
assert !internal.outline.enabled;
assert !internal.enableTreeShakingOfLibraryMethodOverrides;
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 726013e..339e80b 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -195,7 +195,6 @@
// Assert some of R8 optimizations are disabled.
assert !internal.inlinerOptions().enableInlining;
assert !internal.enableClassInlining;
- assert internal.getVerticalClassMergerOptions().isDisabled();
assert !internal.enableEnumValueOptimization;
assert !internal.outline.enabled;
assert !internal.enableTreeShakingOfLibraryMethodOverrides;
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 12a84ab..ad074ed 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -499,7 +499,8 @@
assert ArtProfileCompletenessChecker.verify(appView);
- VerticalClassMerger.runIfNecessary(appViewWithLiveness, executorService, timing);
+ VerticalClassMerger.createForInitialClassMerging(appViewWithLiveness)
+ .runIfNecessary(executorService, timing);
HorizontalClassMerger.createForInitialClassMerging(appViewWithLiveness)
.runIfNecessary(
executorService,
@@ -737,6 +738,11 @@
GenericSignatureContextBuilder genericContextBuilderBeforeFinalMerging =
GenericSignatureContextBuilder.create(appView);
+ if (appView.hasLiveness()) {
+ VerticalClassMerger.createForFinalClassMerging(appView.withLiveness())
+ .runIfNecessary(executorService, timing);
+ }
+
// Run horizontal class merging. This runs even if shrinking is disabled to ensure synthetics
// are always merged.
HorizontalClassMerger.createForFinalClassMerging(appView)
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index d2647bb..405f214 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -54,6 +54,7 @@
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.SystemPropertyUtils;
import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
@@ -132,7 +133,10 @@
private GraphConsumer mainDexKeptGraphConsumer = null;
private InputDependencyGraphConsumer inputDependencyGraphConsumer = null;
private final FeatureSplitConfiguration.Builder featureSplitConfigurationBuilder =
- FeatureSplitConfiguration.builder();
+ FeatureSplitConfiguration.builder()
+ .setEnableIsolatedSplits(
+ SystemPropertyUtils.parseSystemPropertyOrDefault(
+ "com.android.tools.r8.isolatedSplits", false));
private String synthesizedClassPrefix = "";
private boolean enableMissingLibraryApiModeling = false;
private boolean enableExperimentalKeepAnnotations =
@@ -1331,6 +1335,9 @@
.setMainDexKeepRules(mainDexKeepRules)
.setDesugaredLibraryConfiguration(desugaredLibrarySpecification)
.setEnableMissingLibraryApiModeling(enableMissingLibraryApiModeling)
+ .applyIf(
+ featureSplitConfiguration != null,
+ b -> b.setIsolatedSplits(featureSplitConfiguration.isIsolatedSplitsEnabled()))
.build();
}
diff --git a/src/main/java/com/android/tools/r8/classmerging/ClassMergerMode.java b/src/main/java/com/android/tools/r8/classmerging/ClassMergerMode.java
new file mode 100644
index 0000000..bb9a37b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/classmerging/ClassMergerMode.java
@@ -0,0 +1,17 @@
+// 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.classmerging;
+
+public enum ClassMergerMode {
+ INITIAL,
+ FINAL;
+
+ public boolean isInitial() {
+ return this == INITIAL;
+ }
+
+ public boolean isFinal() {
+ return this == FINAL;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/classmerging/ClassMergerTreeFixer.java b/src/main/java/com/android/tools/r8/classmerging/ClassMergerTreeFixer.java
index 8bf6828..0417118 100644
--- a/src/main/java/com/android/tools/r8/classmerging/ClassMergerTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/classmerging/ClassMergerTreeFixer.java
@@ -184,8 +184,9 @@
if (newMethodSignature == null) {
newMethodSignature = fixupMethodReference(originalMethodReference).getSignature();
- // If the signature is already reserved by another interface, find a fresh one.
- if (reservedInterfaceSignatures.containsValue(newMethodSignature)) {
+ // If the signature is kept or already reserved by another interface, find a fresh one.
+ if (keptSignatures.contains(newMethodSignature)
+ || reservedInterfaceSignatures.containsValue(newMethodSignature)) {
DexString name =
dexItemFactory.createGloballyFreshMemberString(
originalMethodReference.getName().toSourceString());
diff --git a/src/main/java/com/android/tools/r8/dump/DumpOptions.java b/src/main/java/com/android/tools/r8/dump/DumpOptions.java
index ac46112..c52f655 100644
--- a/src/main/java/com/android/tools/r8/dump/DumpOptions.java
+++ b/src/main/java/com/android/tools/r8/dump/DumpOptions.java
@@ -26,6 +26,7 @@
import java.util.Objects;
import java.util.Optional;
import java.util.TreeMap;
+import java.util.function.Consumer;
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public class DumpOptions {
@@ -45,6 +46,7 @@
private static final String DESUGAR_STATE_KEY = "desugar-state";
private static final String INTERMEDIATE_KEY = "intermediate";
private static final String INCLUDE_DATA_RESOURCES_KEY = "include-data-resources";
+ private static final String ISOLATED_SPLITS_KEY = "isolated-splits";
private static final String TREE_SHAKING_KEY = "tree-shaking";
private static final String MINIFICATION_KEY = "minification";
private static final String FORCE_PROGUARD_COMPATIBILITY_KEY = "force-proguard-compatibility";
@@ -63,6 +65,7 @@
private final DesugarState desugarState;
private final Optional<Boolean> intermediate;
private final Optional<Boolean> includeDataResources;
+ private final Optional<Boolean> isolatedSplits;
private final Optional<Boolean> treeShaking;
private final Optional<Boolean> minification;
private final Optional<Boolean> forceProguardCompatibility;
@@ -96,6 +99,7 @@
DesugarState desugarState,
Optional<Boolean> intermediate,
Optional<Boolean> includeDataResources,
+ Optional<Boolean> isolatedSplits,
Optional<Boolean> treeShaking,
Optional<Boolean> minification,
Optional<Boolean> forceProguardCompatibility,
@@ -120,6 +124,7 @@
this.desugarState = desugarState;
this.intermediate = intermediate;
this.includeDataResources = includeDataResources;
+ this.isolatedSplits = isolatedSplits;
this.treeShaking = treeShaking;
this.minification = minification;
this.forceProguardCompatibility = forceProguardCompatibility;
@@ -167,6 +172,7 @@
}
addOptionalDumpEntry(buildProperties, INTERMEDIATE_KEY, intermediate);
addOptionalDumpEntry(buildProperties, INCLUDE_DATA_RESOURCES_KEY, includeDataResources);
+ addOptionalDumpEntry(buildProperties, ISOLATED_SPLITS_KEY, isolatedSplits);
addOptionalDumpEntry(buildProperties, TREE_SHAKING_KEY, treeShaking);
addOptionalDumpEntry(
buildProperties, FORCE_PROGUARD_COMPATIBILITY_KEY, forceProguardCompatibility);
@@ -239,6 +245,9 @@
case INCLUDE_DATA_RESOURCES_KEY:
builder.setIncludeDataResources(Optional.of(Boolean.parseBoolean(value)));
return;
+ case ISOLATED_SPLITS_KEY:
+ builder.setIsolatedSplits(Boolean.parseBoolean(value));
+ return;
case TREE_SHAKING_KEY:
builder.setTreeShaking(Boolean.parseBoolean(value));
return;
@@ -355,6 +364,7 @@
private DesugarState desugarState;
private Optional<Boolean> intermediate = Optional.empty();
private Optional<Boolean> includeDataResources = Optional.empty();
+ private Optional<Boolean> isolatedSplits = Optional.empty();
private Optional<Boolean> treeShaking = Optional.empty();
private Optional<Boolean> minification = Optional.empty();
private Optional<Boolean> forceProguardCompatibility = Optional.empty();
@@ -379,6 +389,13 @@
public Builder() {}
+ public Builder applyIf(boolean condition, Consumer<Builder> thenConsumer) {
+ if (condition) {
+ thenConsumer.accept(this);
+ }
+ return this;
+ }
+
public Builder setBackend(Backend backend) {
this.backend = backend;
return this;
@@ -435,6 +452,11 @@
return this;
}
+ public Builder setIsolatedSplits(boolean isolatedSplits) {
+ this.isolatedSplits = Optional.of(isolatedSplits);
+ return this;
+ }
+
public Builder setForceProguardCompatibility(boolean forceProguardCompatibility) {
this.forceProguardCompatibility = Optional.of(forceProguardCompatibility);
return this;
@@ -530,6 +552,7 @@
desugarState,
intermediate,
includeDataResources,
+ isolatedSplits,
treeShaking,
minification,
forceProguardCompatibility,
diff --git a/src/main/java/com/android/tools/r8/features/FeatureSplitBoundaryOptimizationUtils.java b/src/main/java/com/android/tools/r8/features/FeatureSplitBoundaryOptimizationUtils.java
index 4c5c33b..9ac1bd5 100644
--- a/src/main/java/com/android/tools/r8/features/FeatureSplitBoundaryOptimizationUtils.java
+++ b/src/main/java/com/android/tools/r8/features/FeatureSplitBoundaryOptimizationUtils.java
@@ -42,10 +42,21 @@
if (!classToFeatureSplitMap.isInBase(definition, appView)) {
return false;
}
- // If isolated splits are enabled then the resolved method must be public.
- if (appView.options().getFeatureSplitConfiguration().isIsolatedSplitsEnabled()
- && !definition.getAccessFlags().isPublic()) {
- return false;
+ // If isolated splits are enabled then the resolved item must be either (1) public or (2)
+ // a protected member and the access is in a subclass of the resolved member's holder.
+ if (appView.options().getFeatureSplitConfiguration().isIsolatedSplitsEnabled()) {
+ if (definition.isClass()) {
+ if (!definition.getAccessFlags().isPublic()) {
+ return false;
+ }
+ } else if (definition.getAccessFlags().isPackagePrivateOrProtected()) {
+ if (definition.getAccessFlags().isPackagePrivate()
+ || !appView
+ .appInfo()
+ .isSubtype(context.getContextClass(), definition.getContextClass())) {
+ return false;
+ }
+ }
}
return true;
}
diff --git a/src/main/java/com/android/tools/r8/features/IsolatedFeatureSplitsChecker.java b/src/main/java/com/android/tools/r8/features/IsolatedFeatureSplitsChecker.java
index 1043723..376acee 100644
--- a/src/main/java/com/android/tools/r8/features/IsolatedFeatureSplitsChecker.java
+++ b/src/main/java/com/android/tools/r8/features/IsolatedFeatureSplitsChecker.java
@@ -80,6 +80,10 @@
|| features.isInSameFeature(accessedItem, context, appView)) {
return;
}
+ if (accessedItem.getAccessFlags().isProtected()
+ && appView.appInfo().isSubtype(context.getContextClass(), accessedItem.getContextClass())) {
+ return;
+ }
DontWarnConfiguration dontWarnConfiguration = appView.getDontWarnConfiguration();
if (dontWarnConfiguration.matches(accessedItem) || dontWarnConfiguration.matches(context)) {
return;
diff --git a/src/main/java/com/android/tools/r8/graph/AccessControl.java b/src/main/java/com/android/tools/r8/graph/AccessControl.java
index 3ed6de7..2cceb1a 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessControl.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessControl.java
@@ -78,20 +78,19 @@
}
return classAccessibility;
}
+ if (!member.getHolderType().isSamePackage(context.getContextType())) {
+ if (memberFlags.isPackagePrivate()
+ || !appInfo.isSubtype(context.getContextType(), member.getHolderType())) {
+ return OptionalBool.FALSE;
+ }
+ }
if (appView.hasClassHierarchy()
&& context.isProgramDefinition()
&& !FeatureSplitBoundaryOptimizationUtils.isSafeForAccess(
member, context.asProgramDefinition(), appView.withClassHierarchy())) {
return OptionalBool.UNKNOWN;
}
- if (member.getHolderType().isSamePackage(context.getContextType())) {
- return classAccessibility;
- }
- if (memberFlags.isProtected()
- && appInfo.isSubtype(context.getContextType(), member.getHolderType())) {
- return classAccessibility;
- }
- return OptionalBool.FALSE;
+ return classAccessibility;
}
private static boolean isNestMate(DexClass clazz, DexClass context) {
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index dc2094f..05c6fa8 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.DesugarGraphConsumer;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.origin.GlobalSyntheticOrigin;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -295,8 +295,7 @@
: FieldResolutionResult.unknown();
}
- public void notifyHorizontalClassMergerFinished(
- HorizontalClassMerger.Mode horizontalClassMergerMode) {
+ public void notifyHorizontalClassMergerFinished(ClassMergerMode horizontalClassMergerMode) {
// Intentionally empty.
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index a9ebda5..691680c 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
import com.android.tools.r8.androidapi.ComputedApiLevel;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.contexts.CompilationContext;
import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
import com.android.tools.r8.errors.dontwarn.DontWarnConfiguration;
@@ -19,7 +20,6 @@
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.graph.lens.InitClassLens;
import com.android.tools.r8.graph.lens.NonIdentityGraphLens;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraintFactory;
import com.android.tools.r8.ir.analysis.proto.EnumLiteProtoShrinker;
@@ -843,7 +843,7 @@
}
public void setHorizontallyMergedClasses(
- HorizontallyMergedClasses horizontallyMergedClasses, HorizontalClassMerger.Mode mode) {
+ HorizontallyMergedClasses horizontallyMergedClasses, ClassMergerMode mode) {
assert !hasHorizontallyMergedClasses() || mode.isFinal();
this.horizontallyMergedClasses = horizontallyMergedClasses().extend(horizontallyMergedClasses);
testing()
@@ -863,10 +863,16 @@
return verticallyMergedClasses;
}
- public void setVerticallyMergedClasses(VerticallyMergedClasses verticallyMergedClasses) {
- assert this.verticallyMergedClasses == null;
- this.verticallyMergedClasses = verticallyMergedClasses;
- testing().verticallyMergedClassesConsumer.accept(dexItemFactory(), verticallyMergedClasses);
+ public void setVerticallyMergedClasses(
+ VerticallyMergedClasses verticallyMergedClasses, ClassMergerMode mode) {
+ if (mode.isInitial()) {
+ assert this.verticallyMergedClasses == null;
+ this.verticallyMergedClasses = verticallyMergedClasses;
+ testing().verticallyMergedClassesConsumer.accept(dexItemFactory(), verticallyMergedClasses);
+ } else {
+ assert this.verticallyMergedClasses != null;
+ assert verticallyMergedClasses.isEmpty();
+ }
}
public OpenClosedInterfacesCollection getOpenClosedInterfacesCollection() {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index 2097c3a..cb4af41 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -7,6 +7,7 @@
import static com.google.common.base.Predicates.not;
import com.android.tools.r8.androidapi.ComputedApiLevel;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.classmerging.SyntheticArgumentClass;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
@@ -25,7 +26,6 @@
import com.android.tools.r8.graph.ProgramMember;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.code.ClassInitializerMerger;
import com.android.tools.r8.horizontalclassmerging.code.SyntheticInitializerConverter;
import com.android.tools.r8.ir.analysis.value.NumberFromIntervalValue;
@@ -59,7 +59,7 @@
private static final OptimizationFeedback feedback = OptimizationFeedbackSimple.getInstance();
private final AppView<?> appView;
- private final Mode mode;
+ private final ClassMergerMode mode;
private final HorizontalMergeGroup group;
private final DexItemFactory dexItemFactory;
private final HorizontalClassMergerGraphLens.Builder lensBuilder;
@@ -79,7 +79,7 @@
private ClassMerger(
AppView<?> appView,
IRCodeProvider codeProvider,
- Mode mode,
+ ClassMergerMode mode,
HorizontalClassMergerGraphLens.Builder lensBuilder,
HorizontalMergeGroup group,
Collection<VirtualMethodMerger> virtualMethodMergers) {
@@ -375,11 +375,14 @@
public static class Builder {
private final AppView<?> appView;
private final IRCodeProvider codeProvider;
- private final Mode mode;
+ private final ClassMergerMode mode;
private final HorizontalMergeGroup group;
public Builder(
- AppView<?> appView, IRCodeProvider codeProvider, HorizontalMergeGroup group, Mode mode) {
+ AppView<?> appView,
+ IRCodeProvider codeProvider,
+ HorizontalMergeGroup group,
+ ClassMergerMode mode) {
this.appView = appView;
this.codeProvider = codeProvider;
this.group = group;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index d7c99ba..03d62ea 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.graph.DexClassAndMethod.asProgramMethodOrNull;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.classmerging.Policy;
import com.android.tools.r8.classmerging.SyntheticArgumentClass;
import com.android.tools.r8.graph.AppInfo;
@@ -40,24 +41,11 @@
public class HorizontalClassMerger {
- public enum Mode {
- INITIAL,
- FINAL;
-
- public boolean isInitial() {
- return this == INITIAL;
- }
-
- public boolean isFinal() {
- return this == FINAL;
- }
- }
-
private final AppView<?> appView;
- private final Mode mode;
+ private final ClassMergerMode mode;
private final HorizontalClassMergerOptions options;
- private HorizontalClassMerger(AppView<?> appView, Mode mode) {
+ private HorizontalClassMerger(AppView<?> appView, ClassMergerMode mode) {
this.appView = appView;
this.mode = mode;
this.options = appView.options().horizontalClassMergerOptions();
@@ -65,17 +53,17 @@
public static HorizontalClassMerger createForInitialClassMerging(
AppView<AppInfoWithLiveness> appView) {
- return new HorizontalClassMerger(appView, Mode.INITIAL);
+ return new HorizontalClassMerger(appView, ClassMergerMode.INITIAL);
}
public static HorizontalClassMerger createForFinalClassMerging(
AppView<? extends AppInfoWithClassHierarchy> appView) {
- return new HorizontalClassMerger(appView, Mode.FINAL);
+ return new HorizontalClassMerger(appView, ClassMergerMode.FINAL);
}
public static HorizontalClassMerger createForD8ClassMerging(AppView<?> appView) {
assert appView.options().horizontalClassMergerOptions().isRestrictedToSynthetics();
- return new HorizontalClassMerger(appView, Mode.FINAL);
+ return new HorizontalClassMerger(appView, ClassMergerMode.FINAL);
}
public void runIfNecessary(ExecutorService executorService, Timing timing)
@@ -113,7 +101,7 @@
}
private MutableMethodConversionOptions getConversionOptions() {
- return mode == Mode.INITIAL
+ return mode == ClassMergerMode.INITIAL
? MethodConversionOptions.forPreLirPhase(appView)
: MethodConversionOptions.forPostLirPhase(appView);
}
@@ -423,7 +411,7 @@
private HorizontalClassMergerGraphLens createLens(
HorizontallyMergedClasses mergedClasses,
HorizontalClassMergerGraphLens.Builder lensBuilder,
- Mode mode,
+ ClassMergerMode mode,
ProfileCollectionAdditions profileCollectionAdditions,
SyntheticArgumentClass syntheticArgumentClass,
ExecutorService executorService,
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerTreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerTreeFixer.java
index fb27321..5bc07a2 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerTreeFixer.java
@@ -4,10 +4,10 @@
package com.android.tools.r8.horizontalclassmerging;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.classmerging.ClassMergerTreeFixer;
import com.android.tools.r8.classmerging.SyntheticArgumentClass;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
import com.android.tools.r8.utils.Timing;
import java.util.concurrent.ExecutionException;
@@ -24,13 +24,13 @@
HorizontalClassMergerGraphLens,
HorizontallyMergedClasses> {
- private final Mode mode;
+ private final ClassMergerMode mode;
public HorizontalClassMergerTreeFixer(
AppView<?> appView,
HorizontallyMergedClasses mergedClasses,
HorizontalClassMergerGraphLens.Builder lensBuilder,
- Mode mode,
+ ClassMergerMode mode,
ProfileCollectionAdditions profileCollectionAdditions,
SyntheticArgumentClass syntheticArgumentClass) {
super(appView, lensBuilder, mergedClasses, profileCollectionAdditions, syntheticArgumentClass);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
index b522877..2355e0b 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.dex.Constants.TEMPORARY_INSTANCE_INITIALIZER_PREFIX;
import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.classmerging.SyntheticArgumentClass;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -19,7 +20,6 @@
import com.android.tools.r8.graph.DexTypeUtils;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.code.ConstructorEntryPointSynthesizedCode;
import com.android.tools.r8.horizontalclassmerging.code.SyntheticInitializerConverter;
import com.android.tools.r8.ir.conversion.ExtraConstantIntParameter;
@@ -49,7 +49,7 @@
private final List<ProgramMethod> instanceInitializers;
private final InstanceInitializerDescription instanceInitializerDescription;
private final HorizontalClassMergerGraphLens.Builder lensBuilder;
- private final Mode mode;
+ private final ClassMergerMode mode;
InstanceInitializerMerger(
AppView<? extends AppInfoWithClassHierarchy> appView,
@@ -57,7 +57,7 @@
HorizontalMergeGroup group,
List<ProgramMethod> instanceInitializers,
HorizontalClassMergerGraphLens.Builder lensBuilder,
- Mode mode) {
+ ClassMergerMode mode) {
this(appView, classIdentifiers, group, instanceInitializers, lensBuilder, mode, null);
}
@@ -67,7 +67,7 @@
HorizontalMergeGroup group,
List<ProgramMethod> instanceInitializers,
HorizontalClassMergerGraphLens.Builder lensBuilder,
- Mode mode,
+ ClassMergerMode mode,
InstanceInitializerDescription instanceInitializerDescription) {
this.appView = appView;
this.classIdentifiers = classIdentifiers;
@@ -175,13 +175,13 @@
private int estimatedDexCodeSize;
private final List<List<ProgramMethod>> instanceInitializerGroups = new ArrayList<>();
private final HorizontalClassMergerGraphLens.Builder lensBuilder;
- private final Mode mode;
+ private final ClassMergerMode mode;
public Builder(
AppView<? extends AppInfoWithClassHierarchy> appView,
Reference2IntMap<DexType> classIdentifiers,
HorizontalClassMergerGraphLens.Builder lensBuilder,
- Mode mode) {
+ ClassMergerMode mode) {
this.appView = appView;
this.classIdentifiers = classIdentifiers;
this.lensBuilder = lensBuilder;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMergerCollection.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMergerCollection.java
index 62d7522..dcb9ee0 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMergerCollection.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMergerCollection.java
@@ -6,12 +6,12 @@
import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.InstanceInitializerMerger.Builder;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
@@ -44,7 +44,7 @@
IRCodeProvider codeProvider,
HorizontalMergeGroup group,
HorizontalClassMergerGraphLens.Builder lensBuilder,
- Mode mode) {
+ ClassMergerMode mode) {
if (!appView.hasClassHierarchy()) {
assert appView.options().horizontalClassMergerOptions().isRestrictedToSynthetics();
assert verifyNoInstanceInitializers(group);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
index fa18dbb..a9fa5b6 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
@@ -4,12 +4,12 @@
package com.android.tools.r8.horizontalclassmerging;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.classmerging.Policy;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.policies.AllInstantiatedOrUninstantiated;
import com.android.tools.r8.horizontalclassmerging.policies.CheckAbstractClasses;
import com.android.tools.r8.horizontalclassmerging.policies.CheckSyntheticClasses;
@@ -76,7 +76,7 @@
public static List<Policy> getPolicies(
AppView<?> appView,
IRCodeProvider codeProvider,
- Mode mode,
+ ClassMergerMode mode,
RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
if (appView.hasClassHierarchy()) {
return getPoliciesForR8(
@@ -86,7 +86,7 @@
}
}
- private static List<Policy> getPoliciesForD8(AppView<AppInfo> appView, Mode mode) {
+ private static List<Policy> getPoliciesForD8(AppView<AppInfo> appView, ClassMergerMode mode) {
assert mode.isFinal();
List<Policy> policies =
ImmutableList.<Policy>builder()
@@ -101,7 +101,7 @@
private static List<Policy> getPoliciesForR8(
AppView<? extends AppInfoWithClassHierarchy> appView,
IRCodeProvider codeProvider,
- Mode mode,
+ ClassMergerMode mode,
RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
List<Policy> policies =
ImmutableList.<Policy>builder()
@@ -115,7 +115,7 @@
private static List<SingleClassPolicy> getSingleClassPolicies(
AppView<? extends AppInfoWithClassHierarchy> appView,
- Mode mode,
+ ClassMergerMode mode,
RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
ImmutableList.Builder<SingleClassPolicy> builder = ImmutableList.builder();
@@ -141,7 +141,7 @@
}
private static List<SingleClassPolicy> getSingleClassPoliciesForD8(
- AppView<AppInfo> appView, Mode mode) {
+ AppView<AppInfo> appView, ClassMergerMode mode) {
ImmutableList.Builder<SingleClassPolicy> builder =
ImmutableList.<SingleClassPolicy>builder()
.add(new CheckSyntheticClasses(appView))
@@ -162,7 +162,7 @@
private static void addSingleClassPoliciesForMergingNonSyntheticClasses(
AppView<AppInfoWithLiveness> appView,
- Mode mode,
+ ClassMergerMode mode,
RuntimeTypeCheckInfo runtimeTypeCheckInfo,
ImmutableList.Builder<SingleClassPolicy> builder) {
builder.add(
@@ -183,7 +183,7 @@
private static boolean verifySingleClassPoliciesIrrelevantForMergingSynthetics(
AppView<? extends AppInfoWithClassHierarchy> appView,
- Mode mode,
+ ClassMergerMode mode,
ImmutableList.Builder<SingleClassPolicy> builder) {
List<SingleClassPolicy> policies =
ImmutableList.of(
@@ -203,7 +203,9 @@
}
private static boolean verifySingleClassPoliciesIrrelevantForMergingSyntheticsInD8(
- AppView<AppInfo> appView, Mode mode, ImmutableList.Builder<SingleClassPolicy> builder) {
+ AppView<AppInfo> appView,
+ ClassMergerMode mode,
+ ImmutableList.Builder<SingleClassPolicy> builder) {
List<SingleClassPolicy> policies =
ImmutableList.of(
new NoResourceClasses(),
@@ -222,7 +224,7 @@
private static List<Policy> getMultiClassPolicies(
AppView<? extends AppInfoWithClassHierarchy> appView,
IRCodeProvider codeProvider,
- Mode mode,
+ ClassMergerMode mode,
RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
ImmutableList.Builder<Policy> builder = ImmutableList.builder();
@@ -262,7 +264,7 @@
}
private static List<? extends Policy> getMultiClassPoliciesForD8(
- AppView<AppInfo> appView, Mode mode) {
+ AppView<AppInfo> appView, ClassMergerMode mode) {
ImmutableList.Builder<MultiClassPolicy> builder = ImmutableList.builder();
builder.add(
new CheckAbstractClasses(appView),
@@ -281,7 +283,7 @@
private static void addRequiredMultiClassPolicies(
AppView<? extends AppInfoWithClassHierarchy> appView,
- Mode mode,
+ ClassMergerMode mode,
RuntimeTypeCheckInfo runtimeTypeCheckInfo,
ImmutableList.Builder<Policy> builder) {
ImmediateProgramSubtypingInfo immediateSubtypingInfo =
@@ -313,7 +315,7 @@
private static void addMultiClassPoliciesForInterfaceMerging(
AppView<? extends AppInfoWithClassHierarchy> appView,
- Mode mode,
+ ClassMergerMode mode,
ImmutableList.Builder<Policy> builder) {
builder.add(
new NoDefaultInterfaceMethodMerging(appView, mode),
@@ -323,7 +325,9 @@
}
private static boolean verifyMultiClassPoliciesIrrelevantForMergingSyntheticsInD8(
- AppView<AppInfo> appView, Mode mode, ImmutableList.Builder<MultiClassPolicy> builder) {
+ AppView<AppInfo> appView,
+ ClassMergerMode mode,
+ ImmutableList.Builder<MultiClassPolicy> builder) {
List<MultiClassPolicy> policies =
ImmutableList.of(new SyntheticItemsPolicy(appView, mode), new SameParentClass());
policies.stream().map(VerifyMultiClassPolicyAlwaysSatisfied::new).forEach(builder::add);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
index d07b48e..2b7d783 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
@@ -4,13 +4,13 @@
package com.android.tools.r8.horizontalclassmerging.code;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.lens.GraphLens;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.IRCodeProvider;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.conversion.IRConverter;
@@ -33,7 +33,7 @@
private final AppView<?> appView;
private final IRCodeProvider codeProvider;
- private final Mode mode;
+ private final ClassMergerMode mode;
private final List<ProgramMethod> classInitializers;
@@ -43,7 +43,7 @@
private SyntheticInitializerConverter(
AppView<?> appView,
IRCodeProvider codeProvider,
- Mode mode,
+ ClassMergerMode mode,
List<ProgramMethod> classInitializers,
Set<DexProgramClass> instanceInitializers) {
this.appView = appView;
@@ -53,7 +53,8 @@
this.instanceInitializers = instanceInitializers;
}
- public static Builder builder(AppView<?> appView, IRCodeProvider codeProvider, Mode mode) {
+ public static Builder builder(
+ AppView<?> appView, IRCodeProvider codeProvider, ClassMergerMode mode) {
return new Builder(appView, codeProvider, mode);
}
@@ -135,12 +136,12 @@
private final AppView<?> appView;
private final IRCodeProvider codeProvider;
- private final Mode mode;
+ private final ClassMergerMode mode;
private final List<ProgramMethod> classInitializers = new ArrayList<>();
private final Set<DexProgramClass> instanceInitializers = Sets.newIdentityHashSet();
- private Builder(AppView<?> appView, IRCodeProvider codeProvider, Mode mode) {
+ private Builder(AppView<?> appView, IRCodeProvider codeProvider, ClassMergerMode mode) {
this.appView = appView;
this.codeProvider = codeProvider;
this.mode = mode;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/AllInstantiatedOrUninstantiated.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/AllInstantiatedOrUninstantiated.java
index e0dc0fe..e5839b6 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/AllInstantiatedOrUninstantiated.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/AllInstantiatedOrUninstantiated.java
@@ -4,9 +4,9 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -14,7 +14,8 @@
private final AppView<AppInfoWithLiveness> appView;
- public AllInstantiatedOrUninstantiated(AppView<AppInfoWithLiveness> appView, Mode mode) {
+ public AllInstantiatedOrUninstantiated(
+ AppView<AppInfoWithLiveness> appView, ClassMergerMode mode) {
// This policy is only used to prevent that horizontal class merging regresses the
// uninstantiated type optimization. Since there won't be any IR processing after the final
// round of horizontal class merging, there is no need to use the policy.
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/FinalizeMergeGroup.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/FinalizeMergeGroup.java
index 16e17f0..1fdb066 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/FinalizeMergeGroup.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/FinalizeMergeGroup.java
@@ -4,9 +4,9 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.HorizontalMergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
import com.android.tools.r8.utils.ListUtils;
@@ -26,9 +26,9 @@
public class FinalizeMergeGroup extends MultiClassPolicy {
private final AppView<?> appView;
- private final Mode mode;
+ private final ClassMergerMode mode;
- public FinalizeMergeGroup(AppView<?> appView, Mode mode) {
+ public FinalizeMergeGroup(AppView<?> appView, ClassMergerMode mode) {
this.appView = appView;
this.mode = mode;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoConstructorCollisions.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoConstructorCollisions.java
index f4b929d..f65b9e8 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoConstructorCollisions.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoConstructorCollisions.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
@@ -11,7 +12,6 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.HorizontalMergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicyWithPreprocessing;
import com.android.tools.r8.utils.ArrayUtils;
@@ -44,7 +44,7 @@
private final AppView<?> appView;
private final DexItemFactory dexItemFactory;
- public NoConstructorCollisions(AppView<?> appView, Mode mode) {
+ public NoConstructorCollisions(AppView<?> appView, ClassMergerMode mode) {
assert mode.isFinal();
this.appView = appView;
this.dexItemFactory = appView.dexItemFactory();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDeadEnumLiteMaps.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDeadEnumLiteMaps.java
index 8d1205d..c1cf238 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDeadEnumLiteMaps.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDeadEnumLiteMaps.java
@@ -4,10 +4,10 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
import com.android.tools.r8.ir.analysis.proto.EnumLiteProtoShrinker;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -18,7 +18,7 @@
private final Set<DexType> deadEnumLiteMaps;
- public NoDeadEnumLiteMaps(AppView<AppInfoWithLiveness> appView, Mode mode) {
+ public NoDeadEnumLiteMaps(AppView<AppInfoWithLiveness> appView, ClassMergerMode mode) {
// This policy is only relevant for the initial round of class merging, since the dead enum lite
// maps have been removed from the application when the final round of class merging runs.
assert mode.isInitial();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java
index f330807..4784ba5 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java
@@ -8,6 +8,7 @@
import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.BottomUpClassHierarchyTraversal;
@@ -18,7 +19,6 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.HorizontalMergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicyWithPreprocessing;
import com.android.tools.r8.horizontalclassmerging.policies.NoDefaultInterfaceMethodCollisions.InterfaceInfo;
@@ -73,10 +73,10 @@
extends MultiClassPolicyWithPreprocessing<Map<DexType, InterfaceInfo>> {
private final AppView<? extends AppInfoWithClassHierarchy> appView;
- private final Mode mode;
+ private final ClassMergerMode mode;
public NoDefaultInterfaceMethodCollisions(
- AppView<? extends AppInfoWithClassHierarchy> appView, Mode mode) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, ClassMergerMode mode) {
this.appView = appView;
this.mode = mode;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodMerging.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodMerging.java
index 318a2af..b4d76dc 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodMerging.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodMerging.java
@@ -4,12 +4,12 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethodSignature;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.HorizontalMergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
import com.android.tools.r8.utils.WorkList;
@@ -35,7 +35,7 @@
private final AppView<?> appView;
private final DexType MULTIPLE_SENTINEL;
- public NoDefaultInterfaceMethodMerging(AppView<?> appView, Mode mode) {
+ public NoDefaultInterfaceMethodMerging(AppView<?> appView, ClassMergerMode mode) {
this.appView = appView;
// Use the java.lang.Object type to indicate more than one interface type, as that type
// itself is not an interface type.
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDirectRuntimeTypeChecks.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDirectRuntimeTypeChecks.java
index d330434..d8424e8 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDirectRuntimeTypeChecks.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDirectRuntimeTypeChecks.java
@@ -4,9 +4,9 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
import com.android.tools.r8.shaking.RuntimeTypeCheckInfo;
import com.android.tools.r8.synthesis.SyntheticItems;
@@ -18,12 +18,12 @@
private final RuntimeTypeCheckInfo runtimeTypeCheckInfo;
private final SyntheticItems syntheticItems;
- public NoDirectRuntimeTypeChecks(AppView<?> appView, Mode mode) {
+ public NoDirectRuntimeTypeChecks(AppView<?> appView, ClassMergerMode mode) {
this(appView, mode, null);
}
public NoDirectRuntimeTypeChecks(
- AppView<?> appView, Mode mode, RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
+ AppView<?> appView, ClassMergerMode mode, RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
assert runtimeTypeCheckInfo != null || mode.isFinal();
this.options = appView.options();
this.runtimeTypeCheckInfo = runtimeTypeCheckInfo;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java
index 872bf09..5126df8 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java
@@ -4,12 +4,12 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -19,7 +19,7 @@
private final AppView<AppInfoWithLiveness> appView;
- public NoIllegalInlining(AppView<AppInfoWithLiveness> appView, Mode mode) {
+ public NoIllegalInlining(AppView<AppInfoWithLiveness> appView, ClassMergerMode mode) {
// This policy is only relevant for the first round of horizontal class merging, since the final
// round of horizontal class merging may not require any inlining.
assert mode.isInitial();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceInitializerMerging.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceInitializerMerging.java
index b792e84..1edca95 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceInitializerMerging.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceInitializerMerging.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
@@ -18,7 +19,6 @@
import com.android.tools.r8.graph.MethodAccessInfoCollection;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.horizontalclassmerging.ClassInstanceFieldsMerger;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.HorizontalMergeGroup;
import com.android.tools.r8.horizontalclassmerging.IRCodeProvider;
import com.android.tools.r8.horizontalclassmerging.InstanceInitializerAnalysis;
@@ -64,7 +64,7 @@
public NoInstanceInitializerMerging(
AppView<? extends AppInfoWithClassHierarchy> appView,
IRCodeProvider codeProvider,
- Mode mode) {
+ ClassMergerMode mode) {
assert mode.isFinal();
this.appView = appView;
this.codeProvider = codeProvider;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInterfaces.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInterfaces.java
index feabe1c..8af3dd9 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInterfaces.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInterfaces.java
@@ -4,18 +4,18 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions;
public class NoInterfaces extends SingleClassPolicy {
- private final Mode mode;
+ private final ClassMergerMode mode;
private final HorizontalClassMergerOptions options;
- public NoInterfaces(AppView<?> appView, Mode mode) {
+ public NoInterfaces(AppView<?> appView, ClassMergerMode mode) {
this.mode = mode;
this.options = appView.options().horizontalClassMergerOptions();
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVerticallyMergedClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVerticallyMergedClasses.java
index 3259fe7..1aa69c7 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVerticallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVerticallyMergedClasses.java
@@ -4,16 +4,16 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
public class NoVerticallyMergedClasses extends SingleClassPolicy {
private final AppView<AppInfoWithLiveness> appView;
- public NoVerticallyMergedClasses(AppView<AppInfoWithLiveness> appView, Mode mode) {
+ public NoVerticallyMergedClasses(AppView<AppInfoWithLiveness> appView, ClassMergerMode mode) {
// This policy is only relevant for the initial round, since all vertically merged classes have
// been removed from the application in the final round of horizontal class merging.
assert mode.isInitial();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java
index 21283e1..22d8773 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
@@ -13,7 +14,6 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.HorizontalMergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
import com.android.tools.r8.utils.IterableUtils;
@@ -34,7 +34,8 @@
private final AppView<? extends AppInfoWithClassHierarchy> appView;
- public NoVirtualMethodMerging(AppView<? extends AppInfoWithClassHierarchy> appView, Mode mode) {
+ public NoVirtualMethodMerging(
+ AppView<? extends AppInfoWithClassHierarchy> appView, ClassMergerMode mode) {
assert mode.isFinal();
this.appView = appView;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java
index db74f25..3714024 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java
@@ -6,13 +6,13 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.SubtypingInfo;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.HorizontalMergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicyWithPreprocessing;
import com.android.tools.r8.utils.SetUtils;
@@ -59,13 +59,13 @@
extends MultiClassPolicyWithPreprocessing<SubtypingInfo> {
private final AppView<? extends AppInfoWithClassHierarchy> appView;
- private final Mode mode;
+ private final ClassMergerMode mode;
// The interface merge groups that this policy has committed to so far.
private final Map<DexProgramClass, HorizontalMergeGroup> committed = new IdentityHashMap<>();
public OnlyDirectlyConnectedOrUnrelatedInterfaces(
- AppView<? extends AppInfoWithClassHierarchy> appView, Mode mode) {
+ AppView<? extends AppInfoWithClassHierarchy> appView, ClassMergerMode mode) {
this.appView = appView;
this.mode = mode;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
index b891bcd..72d2468 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
@@ -4,12 +4,12 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethodSignature;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.HorizontalMergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -92,7 +92,7 @@
private final AppView<AppInfoWithLiveness> appView;
- public PreserveMethodCharacteristics(AppView<AppInfoWithLiveness> appView, Mode mode) {
+ public PreserveMethodCharacteristics(AppView<AppInfoWithLiveness> appView, ClassMergerMode mode) {
// This policy checks that method merging does invalidate various properties. Thus there is no
// reason to run this policy if method merging is not allowed.
assert mode.isInitial();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
index bfc4cd1..a7e3ccc 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
@@ -14,7 +15,6 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.HorizontalMergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
import com.android.tools.r8.utils.TraversalContinuation;
@@ -28,9 +28,10 @@
public class RespectPackageBoundaries extends MultiClassPolicy {
private final AppView<? extends AppInfoWithClassHierarchy> appView;
- private final Mode mode;
+ private final ClassMergerMode mode;
- public RespectPackageBoundaries(AppView<? extends AppInfoWithClassHierarchy> appView, Mode mode) {
+ public RespectPackageBoundaries(
+ AppView<? extends AppInfoWithClassHierarchy> appView, ClassMergerMode mode) {
this.appView = appView;
this.mode = mode;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameInstanceFields.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameInstanceFields.java
index da0951e..3e82768 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameInstanceFields.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameInstanceFields.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
@@ -11,7 +12,6 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldAccessFlags;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
import com.android.tools.r8.horizontalclassmerging.policies.SameInstanceFields.InstanceFieldInfo;
import com.google.common.collect.HashMultiset;
@@ -21,9 +21,10 @@
public class SameInstanceFields extends MultiClassSameReferencePolicy<Multiset<InstanceFieldInfo>> {
private final DexItemFactory dexItemFactory;
- private final Mode mode;
+ private final ClassMergerMode mode;
- public SameInstanceFields(AppView<? extends AppInfoWithClassHierarchy> appView, Mode mode) {
+ public SameInstanceFields(
+ AppView<? extends AppInfoWithClassHierarchy> appView, ClassMergerMode mode) {
this.dexItemFactory = appView.dexItemFactory();
this.mode = mode;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java
index 95764f9..dd3b701 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java
@@ -4,9 +4,9 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
import com.android.tools.r8.horizontalclassmerging.policies.SyntheticItemsPolicy.ClassKind;
import com.android.tools.r8.synthesis.SyntheticItems;
@@ -18,10 +18,10 @@
NOT_SYNTHETIC
}
- private final Mode mode;
+ private final ClassMergerMode mode;
private final SyntheticItems syntheticItems;
- public SyntheticItemsPolicy(AppView<?> appView, Mode mode) {
+ public SyntheticItemsPolicy(AppView<?> appView, ClassMergerMode mode) {
this.mode = mode;
this.syntheticItems = appView.getSyntheticItems();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 1ed5f9a..734004b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -62,7 +62,7 @@
import java.util.Optional;
import java.util.Set;
-public final class DefaultInliningOracle implements InliningOracle, InliningStrategy {
+public final class DefaultInliningOracle implements InliningOracle {
private final AppView<AppInfoWithLiveness> appView;
private final InternalOptions options;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
index 9cb641e..5dc2341 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
@@ -20,7 +20,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Map;
-final class ForcedInliningOracle implements InliningOracle, InliningStrategy {
+final class ForcedInliningOracle implements InliningOracle {
private final AppView<AppInfoWithLiveness> appView;
private final Map<? extends InvokeMethod, Inliner.InliningInfo> invokesToInline;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 9f25637..612b305 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -51,7 +51,6 @@
import com.android.tools.r8.ir.conversion.PostMethodProcessor;
import com.android.tools.r8.ir.optimize.SimpleDominatingEffectAnalysis.SimpleEffectAnalysisResult;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.ir.optimize.inliner.DefaultInliningReasonStrategy;
import com.android.tools.r8.ir.optimize.inliner.InliningIRProvider;
import com.android.tools.r8.ir.optimize.inliner.InliningReasonStrategy;
@@ -905,19 +904,13 @@
ProgramMethod method,
IRCode code,
Map<? extends InvokeMethod, InliningInfo> invokesToInline,
+ OptimizationFeedback feedback,
InliningIRProvider inliningIRProvider,
MethodProcessor methodProcessor,
Timing timing) {
ForcedInliningOracle oracle = new ForcedInliningOracle(appView, invokesToInline);
performInliningImpl(
- oracle,
- oracle,
- method,
- code,
- OptimizationFeedbackIgnore.getInstance(),
- inliningIRProvider,
- methodProcessor,
- timing);
+ oracle, method, code, feedback, inliningIRProvider, methodProcessor, timing);
}
public void performInlining(
@@ -953,7 +946,7 @@
new InliningIRProvider(appView, method, code, lensCodeRewriter, methodProcessor);
assert inliningIRProvider.verifyIRCacheIsEmpty();
performInliningImpl(
- oracle, oracle, method, code, feedback, inliningIRProvider, methodProcessor, timing);
+ oracle, method, code, feedback, inliningIRProvider, methodProcessor, timing);
}
public InliningReasonStrategy createDefaultInliningReasonStrategy(
@@ -990,7 +983,6 @@
}
private void performInliningImpl(
- InliningStrategy strategy,
InliningOracle oracle,
ProgramMethod context,
IRCode code,
@@ -1074,13 +1066,13 @@
continue;
}
- if (!strategy.stillHasBudget(action, whyAreYouNotInliningReporter)) {
+ if (!singleTargetOracle.stillHasBudget(action, whyAreYouNotInliningReporter)) {
assert whyAreYouNotInliningReporter.unsetReasonHasBeenReportedFlag();
continue;
}
IRCode inlinee = action.buildInliningIR(appView, invoke, context, inliningIRProvider);
- if (strategy.willExceedBudget(
+ if (singleTargetOracle.willExceedBudget(
action, code, inlinee, invoke, block, whyAreYouNotInliningReporter)) {
assert whyAreYouNotInliningReporter.unsetReasonHasBeenReportedFlag();
continue;
@@ -1094,7 +1086,11 @@
// Inline the inlinee code in place of the invoke instruction
// Back up before the invoke instruction.
iterator.previous();
- strategy.markInlined(inlinee);
+
+ // Intentionally not using singleTargetOracle here, so that we decrease the inlining
+ // instruction allowance on the default inlining oracle when force inlining methods.
+ oracle.markInlined(inlinee);
+
iterator.inlineInvoke(
appView, code, inlinee, blockIterator, blocksToRemove, action.getDowncastClass());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
index b7f06c8..69d5ecf 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
@@ -4,28 +4,36 @@
package com.android.tools.r8.ir.optimize;
+import com.android.tools.r8.graph.AccessControl;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
import com.android.tools.r8.ir.optimize.Inliner.InlineResult;
import com.android.tools.r8.ir.optimize.inliner.InliningIRProvider;
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
/**
* The InliningOracle contains information needed for when inlining other methods into @method.
*/
public interface InliningOracle {
- boolean isForcedInliningOracle();
+ AppView<AppInfoWithLiveness> appView();
- // TODO(b/142116551): This should be equivalent to invoke.lookupSingleTarget(appView, context)!
- ProgramMethod lookupSingleTarget(InvokeMethod invoke, ProgramMethod context);
-
- boolean passesInliningConstraints(
- SingleResolutionResult<?> resolutionResult,
- ProgramMethod candidate,
+ boolean canInlineInstanceInitializer(
+ IRCode code,
+ InvokeDirect invoke,
+ ProgramMethod singleTarget,
+ InliningIRProvider inliningIRProvider,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter);
InlineResult computeInlining(
@@ -37,4 +45,72 @@
ClassInitializationAnalysis classInitializationAnalysis,
InliningIRProvider inliningIRProvider,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter);
+
+ default DexProgramClass getDowncastTypeIfNeeded(InvokeMethod invoke, ProgramMethod target) {
+ if (invoke.isInvokeMethodWithReceiver()) {
+ // If the invoke has a receiver but the actual type of the receiver is different from the
+ // computed target holder, inlining requires a downcast of the receiver. In case we don't know
+ // the exact type of the receiver we use the static type of the receiver.
+ Value receiver = invoke.asInvokeMethodWithReceiver().getReceiver();
+ if (!receiver.getType().isClassType()) {
+ return target.getHolder();
+ }
+
+ ClassTypeElement receiverType =
+ getReceiverTypeOrDefault(invoke, receiver.getType().asClassType());
+ ClassTypeElement targetType = target.getHolderType().toTypeElement(appView()).asClassType();
+ if (!receiverType.lessThanOrEqualUpToNullability(targetType, appView())) {
+ return target.getHolder();
+ }
+ }
+ return null;
+ }
+
+ ClassTypeElement getReceiverTypeOrDefault(InvokeMethod invoke, ClassTypeElement defaultValue);
+
+ boolean isForcedInliningOracle();
+
+ // TODO(b/142116551): This should be equivalent to invoke.lookupSingleTarget(appView, context)!
+ ProgramMethod lookupSingleTarget(InvokeMethod invoke, ProgramMethod context);
+
+ /** Inform the strategy that the inlinee has been inlined. */
+ void markInlined(IRCode inlinee);
+
+ boolean passesInliningConstraints(
+ SingleResolutionResult<?> resolutionResult,
+ ProgramMethod candidate,
+ WhyAreYouNotInliningReporter whyAreYouNotInliningReporter);
+
+ default boolean setDowncastTypeIfNeeded(
+ AppView<AppInfoWithLiveness> appView,
+ InlineAction.Builder actionBuilder,
+ InvokeMethod invoke,
+ ProgramMethod singleTarget,
+ ProgramMethod context) {
+ DexProgramClass downcastClass = getDowncastTypeIfNeeded(invoke, singleTarget);
+ if (downcastClass != null) {
+ if (AccessControl.isClassAccessible(downcastClass, context, appView).isPossiblyFalse()) {
+ return false;
+ }
+ actionBuilder.setDowncastClass(downcastClass);
+ }
+ return true;
+ }
+
+ /** Return true if there is still budget for inlining into this method. */
+ boolean stillHasBudget(
+ InlineAction action, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter);
+
+ /**
+ * Check if the inlinee will exceed the the budget for inlining size into current method.
+ *
+ * <p>Return true if the strategy will *not* allow inlining.
+ */
+ boolean willExceedBudget(
+ InlineAction action,
+ IRCode code,
+ IRCode inlinee,
+ InvokeMethod invoke,
+ BasicBlock block,
+ WhyAreYouNotInliningReporter whyAreYouNotInliningReporter);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java
deleted file mode 100644
index a7bb682..0000000
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java
+++ /dev/null
@@ -1,90 +0,0 @@
-// 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.ir.optimize;
-
-import com.android.tools.r8.graph.AccessControl;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.InvokeDirect;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
-import com.android.tools.r8.ir.optimize.inliner.InliningIRProvider;
-import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-
-interface InliningStrategy {
-
- AppView<AppInfoWithLiveness> appView();
-
- boolean canInlineInstanceInitializer(
- IRCode code,
- InvokeDirect invoke,
- ProgramMethod singleTarget,
- InliningIRProvider inliningIRProvider,
- WhyAreYouNotInliningReporter whyAreYouNotInliningReporter);
-
- /** Return true if there is still budget for inlining into this method. */
- boolean stillHasBudget(
- InlineAction action, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter);
-
- /**
- * Check if the inlinee will exceed the the budget for inlining size into current method.
- *
- * <p>Return true if the strategy will *not* allow inlining.
- */
- boolean willExceedBudget(
- InlineAction action,
- IRCode code,
- IRCode inlinee,
- InvokeMethod invoke,
- BasicBlock block,
- WhyAreYouNotInliningReporter whyAreYouNotInliningReporter);
-
- /** Inform the strategy that the inlinee has been inlined. */
- void markInlined(IRCode inlinee);
-
- default boolean setDowncastTypeIfNeeded(
- AppView<AppInfoWithLiveness> appView,
- InlineAction.Builder actionBuilder,
- InvokeMethod invoke,
- ProgramMethod singleTarget,
- ProgramMethod context) {
- DexProgramClass downcastClass = getDowncastTypeIfNeeded(invoke, singleTarget);
- if (downcastClass != null) {
- if (AccessControl.isClassAccessible(downcastClass, context, appView).isPossiblyFalse()) {
- return false;
- }
- actionBuilder.setDowncastClass(downcastClass);
- }
- return true;
- }
-
- default DexProgramClass getDowncastTypeIfNeeded(InvokeMethod invoke, ProgramMethod target) {
- if (invoke.isInvokeMethodWithReceiver()) {
- // If the invoke has a receiver but the actual type of the receiver is different from the
- // computed target holder, inlining requires a downcast of the receiver. In case we don't know
- // the exact type of the receiver we use the static type of the receiver.
- Value receiver = invoke.asInvokeMethodWithReceiver().getReceiver();
- if (!receiver.getType().isClassType()) {
- return target.getHolder();
- }
-
- ClassTypeElement receiverType =
- getReceiverTypeOrDefault(invoke, receiver.getType().asClassType());
- ClassTypeElement targetType = target.getHolderType().toTypeElement(appView()).asClassType();
- if (!receiverType.lessThanOrEqualUpToNullability(targetType, appView())) {
- return target.getHolder();
- }
- }
- return null;
- }
-
- ClassTypeElement getReceiverTypeOrDefault(InvokeMethod invoke, ClassTypeElement defaultValue);
-}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index 08ff606..b192f8a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -209,7 +209,8 @@
// Inline the class instance.
AffectedValues affectedValues = new AffectedValues();
try {
- anyInlinedMethods |= processor.processInlining(code, affectedValues, inliningIRProvider);
+ anyInlinedMethods |=
+ processor.processInlining(code, affectedValues, feedback, inliningIRProvider);
} catch (IllegalClassInlinerStateException e) {
// We introduced a user that we cannot handle in the class inliner as a result of force
// inlining. Abort gracefully from class inlining without removing the instance.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index cb5bbbe..256abbb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -63,6 +63,7 @@
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
import com.android.tools.r8.ir.optimize.inliner.InliningIRProvider;
import com.android.tools.r8.ir.optimize.inliner.NopWhyAreYouNotInliningReporter;
@@ -361,13 +362,17 @@
//
// Returns `true` if at least one method was inlined.
boolean processInlining(
- IRCode code, AffectedValues affectedValues, InliningIRProvider inliningIRProvider)
+ IRCode code,
+ AffectedValues affectedValues,
+ OptimizationFeedback feedback,
+ InliningIRProvider inliningIRProvider)
throws IllegalClassInlinerStateException {
// Verify that `eligibleInstance` is not aliased.
assert eligibleInstance == eligibleInstance.getAliasedValue();
- boolean anyInlinedMethods = forceInlineDirectMethodInvocations(code, inliningIRProvider);
- anyInlinedMethods |= forceInlineIndirectMethodInvocations(code, inliningIRProvider);
+ boolean anyInlinedMethods =
+ forceInlineDirectMethodInvocations(code, feedback, inliningIRProvider);
+ anyInlinedMethods |= forceInlineIndirectMethodInvocations(code, feedback, inliningIRProvider);
rebindIndirectEligibleInstanceUsersFromPhis();
removeMiscUsages(code, affectedValues);
@@ -379,13 +384,20 @@
@SuppressWarnings("ReferenceEquality")
private boolean forceInlineDirectMethodInvocations(
- IRCode code, InliningIRProvider inliningIRProvider) throws IllegalClassInlinerStateException {
+ IRCode code, OptimizationFeedback feedback, InliningIRProvider inliningIRProvider)
+ throws IllegalClassInlinerStateException {
if (directMethodCalls.isEmpty()) {
return false;
}
inliner.performForcedInlining(
- method, code, directMethodCalls, inliningIRProvider, methodProcessor, Timing.empty());
+ method,
+ code,
+ directMethodCalls,
+ feedback,
+ inliningIRProvider,
+ methodProcessor,
+ Timing.empty());
// In case we are class inlining an object allocation that does not inherit directly from
// java.lang.Object, we need keep force inlining the constructor until we reach
@@ -432,7 +444,13 @@
}
if (!directMethodCalls.isEmpty()) {
inliner.performForcedInlining(
- method, code, directMethodCalls, inliningIRProvider, methodProcessor, Timing.empty());
+ method,
+ code,
+ directMethodCalls,
+ feedback,
+ inliningIRProvider,
+ methodProcessor,
+ Timing.empty());
}
} while (!directMethodCalls.isEmpty());
}
@@ -442,7 +460,8 @@
@SuppressWarnings("ReferenceEquality")
private boolean forceInlineIndirectMethodInvocations(
- IRCode code, InliningIRProvider inliningIRProvider) throws IllegalClassInlinerStateException {
+ IRCode code, OptimizationFeedback feedback, InliningIRProvider inliningIRProvider)
+ throws IllegalClassInlinerStateException {
if (indirectMethodCallsOnInstance.isEmpty()) {
return false;
}
@@ -513,7 +532,13 @@
if (!methodCallsOnInstance.isEmpty()) {
inliner.performForcedInlining(
- method, code, methodCallsOnInstance, inliningIRProvider, methodProcessor, Timing.empty());
+ method,
+ code,
+ methodCallsOnInstance,
+ feedback,
+ inliningIRProvider,
+ methodProcessor,
+ Timing.empty());
} else {
// TODO(b/315284776): Diagnose if this should be removed or reenabled.
/*assert indirectMethodCallsOnInstance.stream()
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
deleted file mode 100644
index c06c056..0000000
--- a/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (c) 2017, 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.ir.synthetic;
-
-import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.UseRegistry;
-import java.util.function.Consumer;
-
-public abstract class SynthesizedCode extends AbstractSynthesizedCode {
-
- private final SourceCodeProvider sourceCodeProvider;
-
- public SynthesizedCode(SourceCodeProvider sourceCodeProvider) {
- this.sourceCodeProvider = sourceCodeProvider;
- }
-
- @Override
- public SourceCodeProvider getSourceCodeProvider() {
- return sourceCodeProvider;
- }
-
- @Override
- public abstract Consumer<UseRegistry> getRegistryCallback(DexClassAndMethod method);
-}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/VirtualRootMethodsAnalysis.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/VirtualRootMethodsAnalysis.java
index 422b122..dd7eac12 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/VirtualRootMethodsAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/VirtualRootMethodsAnalysis.java
@@ -8,17 +8,16 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexMethodSignature;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorCodeScanner;
import com.android.tools.r8.optimize.argumentpropagation.utils.DepthFirstTopDownClassHierarchyTraversal;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.collections.DexMethodSignatureMap;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.collect.Sets;
import java.util.Collection;
-import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
@@ -45,13 +44,13 @@
}
VirtualRootMethod(ProgramMethod root, VirtualRootMethod parent) {
+ assert root != null;
this.parent = parent;
this.root = root;
}
- @SuppressWarnings("ReferenceEquality")
void addOverride(ProgramMethod override) {
- assert override != root;
+ assert override.getDefinition() != root.getDefinition();
assert override.getMethodSignature().equals(root.getMethodSignature());
overrides.add(override);
if (hasParent()) {
@@ -103,7 +102,7 @@
}
}
- private final Map<DexProgramClass, Map<DexMethodSignature, VirtualRootMethod>>
+ private final Map<DexProgramClass, DexMethodSignatureMap<VirtualRootMethod>>
virtualRootMethodsPerClass = new IdentityHashMap<>();
private final Set<DexMethod> monomorphicVirtualMethods = Sets.newIdentityHashSet();
@@ -138,17 +137,18 @@
@Override
public void visit(DexProgramClass clazz) {
- Map<DexMethodSignature, VirtualRootMethod> state = computeVirtualRootMethodsState(clazz);
+ DexMethodSignatureMap<VirtualRootMethod> state = computeVirtualRootMethodsState(clazz);
virtualRootMethodsPerClass.put(clazz, state);
}
- private Map<DexMethodSignature, VirtualRootMethod> computeVirtualRootMethodsState(
+ private DexMethodSignatureMap<VirtualRootMethod> computeVirtualRootMethodsState(
DexProgramClass clazz) {
- Map<DexMethodSignature, VirtualRootMethod> virtualRootMethodsForClass = new HashMap<>();
+ DexMethodSignatureMap<VirtualRootMethod> virtualRootMethodsForClass =
+ DexMethodSignatureMap.create();
immediateSubtypingInfo.forEachImmediateProgramSuperClass(
clazz,
superclass -> {
- Map<DexMethodSignature, VirtualRootMethod> virtualRootMethodsForSuperclass =
+ DexMethodSignatureMap<VirtualRootMethod> virtualRootMethodsForSuperclass =
virtualRootMethodsPerClass.get(superclass);
virtualRootMethodsForSuperclass.forEach(
(signature, info) ->
@@ -157,11 +157,10 @@
});
clazz.forEachProgramVirtualMethod(
method -> {
- DexMethodSignature signature = method.getMethodSignature();
- if (virtualRootMethodsForClass.containsKey(signature)) {
- virtualRootMethodsForClass.get(signature).getParent().addOverride(method);
+ if (virtualRootMethodsForClass.containsKey(method)) {
+ virtualRootMethodsForClass.get(method).getParent().addOverride(method);
} else {
- virtualRootMethodsForClass.put(signature, new VirtualRootMethod(method));
+ virtualRootMethodsForClass.put(method, new VirtualRootMethod(method));
}
});
return virtualRootMethodsForClass;
@@ -170,7 +169,7 @@
@Override
public void prune(DexProgramClass clazz) {
// Record the overrides for each virtual method that is rooted at this class.
- Map<DexMethodSignature, VirtualRootMethod> virtualRootMethodsForClass =
+ DexMethodSignatureMap<VirtualRootMethod> virtualRootMethodsForClass =
virtualRootMethodsPerClass.remove(clazz);
clazz.forEachProgramVirtualMethod(
rootCandidate -> {
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 6fb0c2f..93ac4f4 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -8,6 +8,7 @@
import static com.android.tools.r8.utils.collections.ThrowingSet.isThrowingSet;
import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
@@ -50,7 +51,6 @@
import com.android.tools.r8.graph.UnknownDispatchTargetLookupResult;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.graph.lens.NonIdentityGraphLens;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
@@ -447,8 +447,7 @@
}
@Override
- public void notifyHorizontalClassMergerFinished(
- HorizontalClassMerger.Mode horizontalClassMergerMode) {
+ public void notifyHorizontalClassMergerFinished(ClassMergerMode horizontalClassMergerMode) {
if (horizontalClassMergerMode.isInitial()) {
getMethodAccessInfoCollection().destroy();
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 8222389..616b98a 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.SyntheticInfoConsumer;
import com.android.tools.r8.SyntheticInfoConsumerData;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.contexts.CompilationContext.UniqueContext;
import com.android.tools.r8.errors.MissingGlobalSyntheticsConsumerDiagnostic;
import com.android.tools.r8.errors.Unreachable;
@@ -36,7 +37,6 @@
import com.android.tools.r8.graph.ProgramOrClasspathDefinition;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.lens.NonIdentityGraphLens;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.references.ClassReference;
@@ -403,7 +403,7 @@
return committed.containsType(type) || pending.definitions.containsKey(type);
}
- public boolean isEligibleForClassMerging(DexProgramClass clazz, HorizontalClassMerger.Mode mode) {
+ public boolean isEligibleForClassMerging(DexProgramClass clazz, ClassMergerMode mode) {
assert isSyntheticClass(clazz);
return mode.isFinal() || isSyntheticLambda(clazz);
}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 6fe8388..26a4087 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -26,6 +26,7 @@
import com.android.tools.r8.Version;
import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.classmerging.Policy;
import com.android.tools.r8.debuginfo.DebugRepresentation;
import com.android.tools.r8.dex.ApplicationReader.ProgramClassConflictResolver;
@@ -66,7 +67,6 @@
import com.android.tools.r8.graph.analysis.ResourceAccessAnalysis;
import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
import com.android.tools.r8.graph.lens.GraphLens;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.analysis.proto.ProtoReferences;
@@ -878,12 +878,12 @@
WholeProgramOptimizations wholeProgramOptimizations = WholeProgramOptimizations.ON;
if (mode.isInitialTreeShaking()) {
return horizontalClassMergerOptions.isEnabled(
- HorizontalClassMerger.Mode.INITIAL, wholeProgramOptimizations)
+ ClassMergerMode.INITIAL, wholeProgramOptimizations)
&& !horizontalClassMergerOptions.isRestrictedToSynthetics();
}
if (mode.isFinalTreeShaking()) {
return horizontalClassMergerOptions.isEnabled(
- HorizontalClassMerger.Mode.FINAL, wholeProgramOptimizations)
+ ClassMergerMode.FINAL, wholeProgramOptimizations)
&& !horizontalClassMergerOptions.isRestrictedToSynthetics();
}
assert false;
@@ -1873,7 +1873,7 @@
}
public boolean isEnabled(
- HorizontalClassMerger.Mode mode, WholeProgramOptimizations wholeProgramOptimizations) {
+ ClassMergerMode mode, WholeProgramOptimizations wholeProgramOptimizations) {
if (!enable || debug || intermediate) {
return false;
}
@@ -1901,7 +1901,7 @@
return enableSyntheticMerging;
}
- public boolean isInterfaceMergingEnabled(HorizontalClassMerger.Mode mode) {
+ public boolean isInterfaceMergingEnabled(ClassMergerMode mode) {
if (!enableInterfaceMerging) {
return false;
}
@@ -2359,7 +2359,7 @@
public Function<AppView<AppInfoWithLiveness>, RepackagingConfiguration>
repackagingConfigurationFactory = DefaultRepackagingConfiguration::new;
- public TriConsumer<DexItemFactory, HorizontallyMergedClasses, HorizontalClassMerger.Mode>
+ public TriConsumer<DexItemFactory, HorizontallyMergedClasses, ClassMergerMode>
horizontallyMergedClassesConsumer = ConsumerUtils.emptyTriConsumer();
public Function<List<Policy>, List<Policy>> horizontalClassMergingPolicyRewriter =
Function.identity();
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureMap.java b/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureMap.java
index 8aa569a..65881a8 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureMap.java
@@ -52,7 +52,11 @@
}
public T put(DexEncodedMethod method, T value) {
- return put(method.getReference(), value);
+ return put(method.getSignature(), value);
+ }
+
+ public T put(DexClassAndMethod method, T value) {
+ return put(method.getMethodSignature(), value);
}
@Override
@@ -163,6 +167,10 @@
return containsKey(method.getSignature());
}
+ public boolean containsKey(DexClassAndMethod method) {
+ return containsKey(method.getMethodSignature());
+ }
+
@Override
public boolean containsKey(Object o) {
return backing.containsKey(o);
@@ -182,6 +190,10 @@
return get(method.getSignature());
}
+ public T get(DexClassAndMethod method) {
+ return get(method.getMethodSignature());
+ }
+
public boolean containsKey(DexMethodSignature signature) {
return backing.containsKey(signature);
}
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
index 9b0626b..f94208d 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
@@ -4,35 +4,28 @@
package com.android.tools.r8.verticalclassmerging;
import static com.android.tools.r8.graph.DexClassAndMethod.asProgramMethodOrNull;
-import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.classmerging.SyntheticArgumentClass;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexReference;
-import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.optimize.argumentpropagation.utils.ProgramClassesBidirectedGraph;
-import com.android.tools.r8.profile.art.ArtProfile;
import com.android.tools.r8.profile.art.ArtProfileCompletenessChecker;
import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepInfoCollection;
-import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.Timing.TimingMerger;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
import java.util.ArrayList;
import java.util.Collection;
@@ -54,130 +47,41 @@
private final AppView<AppInfoWithLiveness> appView;
private final DexItemFactory dexItemFactory;
+ private final ClassMergerMode mode;
private final InternalOptions options;
- public VerticalClassMerger(AppView<AppInfoWithLiveness> appView) {
+ public VerticalClassMerger(AppView<AppInfoWithLiveness> appView, ClassMergerMode mode) {
this.appView = appView;
this.dexItemFactory = appView.dexItemFactory();
+ this.mode = mode;
this.options = appView.options();
}
- // Returns a set of types that must not be merged into other types.
- private Set<DexProgramClass> getPinnedClasses() {
- Set<DexProgramClass> pinnedClasses = Sets.newIdentityHashSet();
-
- // For all pinned fields, also pin the type of the field (because changing the type of the field
- // implicitly changes the signature of the field). Similarly, for all pinned methods, also pin
- // the return type and the parameter types of the method.
- // TODO(b/156715504): Compute referenced-by-pinned in the keep info objects.
- List<DexReference> pinnedReferences = new ArrayList<>();
- KeepInfoCollection keepInfo = appView.getKeepInfo();
- keepInfo.forEachPinnedType(pinnedReferences::add, options);
- keepInfo.forEachPinnedMethod(pinnedReferences::add, options);
- keepInfo.forEachPinnedField(pinnedReferences::add, options);
- extractPinnedClasses(pinnedReferences, pinnedClasses);
-
- for (DexProgramClass clazz : appView.appInfo().classes()) {
- if (Iterables.any(clazz.methods(), method -> method.getAccessFlags().isNative())) {
- markClassAsPinned(clazz, pinnedClasses);
- }
- }
-
- // It is valid to have an invoke-direct instruction in a default interface method that targets
- // another default method in the same interface (see InterfaceMethodDesugaringTests.testInvoke-
- // SpecialToDefaultMethod). However, in a class, that would lead to a verification error.
- // Therefore, we disallow merging such interfaces into their subtypes.
- for (DexMethod signature : appView.appInfo().getVirtualMethodsTargetedByInvokeDirect()) {
- markTypeAsPinned(signature.getHolderType(), pinnedClasses);
- }
-
- // The set of targets that must remain for proper resolution error cases should not be merged.
- // TODO(b/192821424): Can be removed if handled.
- extractPinnedClasses(appView.appInfo().getFailedMethodResolutionTargets(), pinnedClasses);
-
- // The ART profiles may contain method rules that do not exist in the app. These method may
- // refer to classes that will be vertically merged into their unique subtype, but the vertical
- // class merger lens will not contain any mappings for the missing methods in the ART profiles.
- // Therefore, trying to perform a lens lookup on these methods will fail.
- for (ArtProfile artProfile : appView.getArtProfileCollection()) {
- artProfile.forEachRule(
- ConsumerUtils.emptyThrowingConsumer(),
- methodRule -> {
- DexMethod method = methodRule.getMethod();
- if (method.getHolderType().isArrayType()) {
- return;
- }
- DexClass holder =
- appView.appInfo().definitionForWithoutExistenceAssert(method.getHolderType());
- if (method.lookupOnClass(holder) == null) {
- extractPinnedClasses(methodRule.getMethod(), pinnedClasses);
- }
- });
- }
-
- return pinnedClasses;
+ public static VerticalClassMerger createForInitialClassMerging(
+ AppView<AppInfoWithLiveness> appView) {
+ return new VerticalClassMerger(appView, ClassMergerMode.INITIAL);
}
- private <T extends DexReference> void extractPinnedClasses(
- Iterable<T> items, Set<DexProgramClass> pinnedClasses) {
- for (DexReference item : items) {
- extractPinnedClasses(item, pinnedClasses);
- }
+ public static VerticalClassMerger createForFinalClassMerging(
+ AppView<AppInfoWithLiveness> appView) {
+ return new VerticalClassMerger(appView, ClassMergerMode.FINAL);
}
- private void extractPinnedClasses(DexReference reference, Set<DexProgramClass> pinnedClasses) {
- markTypeAsPinned(reference.getContextType(), pinnedClasses);
- reference.accept(
- ConsumerUtils.emptyConsumer(),
- field -> {
- // Pin the type of the field.
- markTypeAsPinned(field.getType(), pinnedClasses);
- },
- method -> {
- // Pin the return type and the parameter types of the method. If we were to merge any of
- // these types into their sub classes, then we would implicitly change the signature of
- // this method.
- for (DexType type : method.getReferencedTypes()) {
- markTypeAsPinned(type, pinnedClasses);
- }
- });
- }
-
- private void markTypeAsPinned(DexType type, Set<DexProgramClass> pinnedClasses) {
- DexType baseType = type.toBaseType(dexItemFactory);
- if (!baseType.isClassType()) {
- return;
- }
-
- DexProgramClass clazz =
- asProgramClassOrNull(appView.appInfo().definitionForWithoutExistenceAssert(baseType));
- if (clazz != null && !appView.getKeepInfo(clazz).isPinned(options)) {
- // We check for the case where the type is pinned according to its keep info, so we only need
- // to add it here if it is not the case.
- markClassAsPinned(clazz, pinnedClasses);
- }
- }
-
- private void markClassAsPinned(DexProgramClass clazz, Set<DexProgramClass> pinnedClasses) {
- pinnedClasses.add(clazz);
- }
-
- public static void runIfNecessary(
- AppView<AppInfoWithLiveness> appView, ExecutorService executorService, Timing timing)
+ public void runIfNecessary(ExecutorService executorService, Timing timing)
throws ExecutionException {
timing.begin("VerticalClassMerger");
- if (shouldRun(appView)) {
- new VerticalClassMerger(appView).run(executorService, timing);
+ if (shouldRun()) {
+ run(executorService, timing);
} else {
- appView.setVerticallyMergedClasses(VerticallyMergedClasses.empty());
+ appView.setVerticallyMergedClasses(VerticallyMergedClasses.empty(), mode);
}
assert appView.hasVerticallyMergedClasses();
assert ArtProfileCompletenessChecker.verify(appView);
timing.end();
}
- private static boolean shouldRun(AppView<AppInfoWithLiveness> appView) {
- return appView.options().getVerticalClassMergerOptions().isEnabled()
+ private boolean shouldRun() {
+ return options.getVerticalClassMergerOptions().isEnabled(mode)
&& !appView.hasCfByteCodePassThroughMethods();
}
@@ -200,7 +104,8 @@
VerticalClassMergerResult verticalClassMergerResult =
mergeClassesInConnectedComponents(
connectedComponents, immediateSubtypingInfo, executorService, timing);
- appView.setVerticallyMergedClasses(verticalClassMergerResult.getVerticallyMergedClasses());
+ appView.setVerticallyMergedClasses(
+ verticalClassMergerResult.getVerticallyMergedClasses(), mode);
if (verticalClassMergerResult.isEmpty()) {
return;
}
@@ -237,15 +142,13 @@
TimingMerger merger = timing.beginMerger("Compute classes to merge", executorService);
List<ConnectedComponentVerticalClassMerger> connectedComponentMergers =
new ArrayList<>(connectedComponents.size());
- Set<DexProgramClass> pinnedClasses = getPinnedClasses();
Collection<Timing> timings =
ThreadUtils.processItemsWithResults(
connectedComponents,
connectedComponent -> {
Timing threadTiming = Timing.create("Compute classes to merge in component", options);
ConnectedComponentVerticalClassMerger connectedComponentMerger =
- new VerticalClassMergerPolicyExecutor(
- appView, immediateSubtypingInfo, pinnedClasses)
+ new VerticalClassMergerPolicyExecutor(appView, immediateSubtypingInfo)
.run(connectedComponent, executorService, threadTiming);
if (!connectedComponentMerger.isEmpty()) {
synchronized (connectedComponentMergers) {
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerOptions.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerOptions.java
index 5205d49..b7f435c 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerOptions.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerOptions.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.verticalclassmerging;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.utils.InternalOptions;
public class VerticalClassMergerOptions {
@@ -19,12 +20,8 @@
setEnabled(false);
}
- public boolean isDisabled() {
- return !isEnabled();
- }
-
- public boolean isEnabled() {
- return enabled && options.isOptimizing() && options.isShrinking();
+ public boolean isEnabled(ClassMergerMode mode) {
+ return enabled && options.isOptimizing() && options.isShrinking() && mode.isInitial();
}
public void setEnabled(boolean enabled) {
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java
index 1874199..6b6f314 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyExecutor.java
@@ -49,15 +49,11 @@
private final AppView<AppInfoWithLiveness> appView;
private final ImmediateProgramSubtypingInfo immediateSubtypingInfo;
- private final Set<DexProgramClass> pinnedClasses;
VerticalClassMergerPolicyExecutor(
- AppView<AppInfoWithLiveness> appView,
- ImmediateProgramSubtypingInfo immediateSubtypingInfo,
- Set<DexProgramClass> pinnedClasses) {
+ AppView<AppInfoWithLiveness> appView, ImmediateProgramSubtypingInfo immediateSubtypingInfo) {
this.appView = appView;
this.immediateSubtypingInfo = immediateSubtypingInfo;
- this.pinnedClasses = pinnedClasses;
}
ConnectedComponentVerticalClassMerger run(
@@ -69,7 +65,7 @@
List.of(
new NoDirectlyInstantiatedClassesPolicy(appView),
new NoInterfacesWithUnknownSubtypesPolicy(appView),
- new NoKeptClassesPolicy(appView, pinnedClasses),
+ new NoKeptClassesPolicy(appView),
new SameFeatureSplitPolicy(appView),
new SameStartupPartitionPolicy(appView),
new NoServiceInterfacesPolicy(appView),
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/policies/NoKeptClassesPolicy.java b/src/main/java/com/android/tools/r8/verticalclassmerging/policies/NoKeptClassesPolicy.java
index 959d28e..c9b0cb5 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/policies/NoKeptClassesPolicy.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/policies/NoKeptClassesPolicy.java
@@ -3,28 +3,40 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.verticalclassmerging.policies;
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.profile.art.ArtProfile;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.KeepInfoCollection;
+import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.verticalclassmerging.VerticalMergeGroup;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
import java.util.Set;
-public class NoKeptClassesPolicy extends VerticalClassMergerPolicy {
+public class NoKeptClassesPolicy
+ extends VerticalClassMergerPolicyWithPreprocessing<Set<DexProgramClass>> {
private final AppView<AppInfoWithLiveness> appView;
- private final Set<DexProgramClass> keptClasses;
private final InternalOptions options;
- public NoKeptClassesPolicy(
- AppView<AppInfoWithLiveness> appView, Set<DexProgramClass> keptClasses) {
+ public NoKeptClassesPolicy(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
- this.keptClasses = keptClasses;
this.options = appView.options();
}
@Override
- public boolean canMerge(VerticalMergeGroup group) {
+ public boolean canMerge(VerticalMergeGroup group, Set<DexProgramClass> keptClasses) {
DexProgramClass sourceClass = group.getSource();
return appView.getKeepInfo(sourceClass).isVerticalClassMergingAllowed(options)
&& !keptClasses.contains(sourceClass);
@@ -34,4 +46,109 @@
public String getName() {
return "NoKeptClassesPolicy";
}
+
+ @Override
+ public Set<DexProgramClass> preprocess(Collection<VerticalMergeGroup> groups) {
+ return getPinnedClasses();
+ }
+
+ // Returns a set of types that must not be merged into other types.
+ private Set<DexProgramClass> getPinnedClasses() {
+ Set<DexProgramClass> pinnedClasses = Sets.newIdentityHashSet();
+
+ // For all pinned fields, also pin the type of the field (because changing the type of the field
+ // implicitly changes the signature of the field). Similarly, for all pinned methods, also pin
+ // the return type and the parameter types of the method.
+ // TODO(b/156715504): Compute referenced-by-pinned in the keep info objects.
+ List<DexReference> pinnedReferences = new ArrayList<>();
+ KeepInfoCollection keepInfo = appView.getKeepInfo();
+ keepInfo.forEachPinnedType(pinnedReferences::add, options);
+ keepInfo.forEachPinnedMethod(pinnedReferences::add, options);
+ keepInfo.forEachPinnedField(pinnedReferences::add, options);
+ extractPinnedClasses(pinnedReferences, pinnedClasses);
+
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ if (Iterables.any(clazz.methods(), method -> method.getAccessFlags().isNative())) {
+ markClassAsPinned(clazz, pinnedClasses);
+ }
+ }
+
+ // It is valid to have an invoke-direct instruction in a default interface method that targets
+ // another default method in the same interface (see InterfaceMethodDesugaringTests.testInvoke-
+ // SpecialToDefaultMethod). However, in a class, that would lead to a verification error.
+ // Therefore, we disallow merging such interfaces into their subtypes.
+ for (DexMethod signature : appView.appInfo().getVirtualMethodsTargetedByInvokeDirect()) {
+ markTypeAsPinned(signature.getHolderType(), pinnedClasses);
+ }
+
+ // The set of targets that must remain for proper resolution error cases should not be merged.
+ // TODO(b/192821424): Can be removed if handled.
+ extractPinnedClasses(appView.appInfo().getFailedMethodResolutionTargets(), pinnedClasses);
+
+ // The ART profiles may contain method rules that do not exist in the app. These method may
+ // refer to classes that will be vertically merged into their unique subtype, but the vertical
+ // class merger lens will not contain any mappings for the missing methods in the ART profiles.
+ // Therefore, trying to perform a lens lookup on these methods will fail.
+ for (ArtProfile artProfile : appView.getArtProfileCollection()) {
+ artProfile.forEachRule(
+ ConsumerUtils.emptyThrowingConsumer(),
+ methodRule -> {
+ DexMethod method = methodRule.getMethod();
+ if (method.getHolderType().isArrayType()) {
+ return;
+ }
+ DexClass holder =
+ appView.appInfo().definitionForWithoutExistenceAssert(method.getHolderType());
+ if (method.lookupOnClass(holder) == null) {
+ extractPinnedClasses(methodRule.getMethod(), pinnedClasses);
+ }
+ });
+ }
+
+ return pinnedClasses;
+ }
+
+ private <T extends DexReference> void extractPinnedClasses(
+ Iterable<T> items, Set<DexProgramClass> pinnedClasses) {
+ for (DexReference item : items) {
+ extractPinnedClasses(item, pinnedClasses);
+ }
+ }
+
+ private void extractPinnedClasses(DexReference reference, Set<DexProgramClass> pinnedClasses) {
+ markTypeAsPinned(reference.getContextType(), pinnedClasses);
+ reference.accept(
+ ConsumerUtils.emptyConsumer(),
+ field -> {
+ // Pin the type of the field.
+ markTypeAsPinned(field.getType(), pinnedClasses);
+ },
+ method -> {
+ // Pin the return type and the parameter types of the method. If we were to merge any of
+ // these types into their sub classes, then we would implicitly change the signature of
+ // this method.
+ for (DexType type : method.getReferencedTypes()) {
+ markTypeAsPinned(type, pinnedClasses);
+ }
+ });
+ }
+
+ private void markTypeAsPinned(DexType type, Set<DexProgramClass> pinnedClasses) {
+ DexType baseType = type.toBaseType(appView.dexItemFactory());
+ if (!baseType.isClassType()) {
+ return;
+ }
+
+ DexProgramClass clazz =
+ asProgramClassOrNull(appView.appInfo().definitionForWithoutExistenceAssert(baseType));
+ if (clazz != null && !appView.getKeepInfo(clazz).isPinned(options)) {
+ // We check for the case where the type is pinned according to its keep info, so we only need
+ // to add it here if it is not the case.
+ markClassAsPinned(clazz, pinnedClasses);
+ }
+ }
+
+ private void markClassAsPinned(DexProgramClass clazz, Set<DexProgramClass> pinnedClasses) {
+ pinnedClasses.add(clazz);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 5349d73..054fb04 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -10,8 +10,8 @@
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.benchmarks.BenchmarkResults;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.debug.DebugTestConfig;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorEventConsumer;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
import com.android.tools.r8.testing.AndroidBuildVersion;
@@ -199,12 +199,12 @@
public T addHorizontallyMergedClassesInspector(
ThrowableConsumer<HorizontallyMergedClassesInspector> inspector) {
- return addHorizontallyMergedClassesInspector(inspector, HorizontalClassMerger.Mode::isFinal);
+ return addHorizontallyMergedClassesInspector(inspector, ClassMergerMode::isFinal);
}
public T addHorizontallyMergedClassesInspector(
ThrowableConsumer<HorizontallyMergedClassesInspector> inspector,
- Predicate<HorizontalClassMerger.Mode> predicate) {
+ Predicate<ClassMergerMode> predicate) {
return addOptionsModification(
options ->
options.testing.horizontallyMergedClassesConsumer =
diff --git a/src/test/java/com/android/tools/r8/features/PackagePrivateIsolatedSplitCrossBoundaryReferenceTest.java b/src/test/java/com/android/tools/r8/features/PackagePrivateIsolatedSplitCrossBoundaryReferenceTest.java
index 1b24d61..10afe1b 100644
--- a/src/test/java/com/android/tools/r8/features/PackagePrivateIsolatedSplitCrossBoundaryReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/features/PackagePrivateIsolatedSplitCrossBoundaryReferenceTest.java
@@ -42,45 +42,43 @@
}
@Test
+ public void testPublicToProtected() throws Exception {
+ MethodReference accessedMethod =
+ Reference.methodFromMethod(NonPublicBase.class.getDeclaredMethod("protectedMethod"));
+ MethodReference main =
+ Reference.methodFromMethod(Feature.class.getDeclaredMethod("testPublicToProtected"));
+ runTestWithExpectedFailure(main, getExpectedDiagnosticMessage(accessedMethod, main));
+ }
+
+ @Test
+ public void testPublicToProtectedFromSubclass() throws Exception {
+ MethodReference main =
+ Reference.methodFromMethod(FeatureSub.class.getDeclaredMethod("testPublicToProtected"));
+ runTest(main, TestDiagnosticMessages::assertNoMessages);
+ }
+
+ @Test
public void testPublicToPublic() throws Exception {
- runTest("testPublicToPublic", TestDiagnosticMessages::assertNoMessages);
+ MethodReference main =
+ Reference.methodFromMethod(Feature.class.getDeclaredMethod("testPublicToPublic"));
+ runTest(main, TestDiagnosticMessages::assertNoMessages);
}
@Test
public void testPublicToNonPublic() throws Exception {
MethodReference accessedMethod =
Reference.methodFromMethod(NonPublicBase.class.getDeclaredMethod("nonPublicMethod"));
- MethodReference context =
+ MethodReference main =
Reference.methodFromMethod(Feature.class.getDeclaredMethod("testPublicToNonPublic"));
- assertFailsCompilationIf(
- enableIsolatedSplits,
- () ->
- runTest(
- "testPublicToNonPublic",
- diagnostics ->
- diagnostics.assertErrorsMatch(
- allOf(
- diagnosticType(IllegalAccessWithIsolatedFeatureSplitsDiagnostic.class),
- diagnosticMessage(
- equalTo(getExpectedDiagnosticMessage(accessedMethod, context)))))));
+ runTestWithExpectedFailure(main, getExpectedDiagnosticMessage(accessedMethod, main));
}
@Test
public void testNonPublicToPublic() throws Exception {
ClassReference accessedClass = Reference.classFromClass(NonPublicBaseSub.class);
- MethodReference context =
+ MethodReference main =
Reference.methodFromMethod(Feature.class.getDeclaredMethod("testNonPublicToPublic"));
- assertFailsCompilationIf(
- enableIsolatedSplits,
- () ->
- runTest(
- "testNonPublicToPublic",
- diagnostics ->
- diagnostics.assertErrorsMatch(
- allOf(
- diagnosticType(IllegalAccessWithIsolatedFeatureSplitsDiagnostic.class),
- diagnosticMessage(
- equalTo(getExpectedDiagnosticMessage(accessedClass, context)))))));
+ runTestWithExpectedFailure(main, getExpectedDiagnosticMessage(accessedClass, main));
}
private static String getExpectedDiagnosticMessage(
@@ -103,13 +101,32 @@
+ ").";
}
- private void runTest(String name, DiagnosticsConsumer inspector) throws Exception {
+ private void runTestWithExpectedFailure(MethodReference main, String expectedDiagnosticMessage)
+ throws Exception {
+ assertFailsCompilationIf(
+ enableIsolatedSplits,
+ () ->
+ runTest(
+ main,
+ diagnostics ->
+ diagnostics.assertErrorsMatch(
+ allOf(
+ diagnosticType(IllegalAccessWithIsolatedFeatureSplitsDiagnostic.class),
+ diagnosticMessage(equalTo(expectedDiagnosticMessage))))));
+ }
+
+ private void runTest(MethodReference main, DiagnosticsConsumer inspector) throws Exception {
testForR8(parameters.getBackend())
.addProgramClasses(NonPublicBase.class, PublicBaseSub.class, NonPublicBaseSub.class)
.addKeepClassAndMembersRules(
NonPublicBase.class, PublicBaseSub.class, NonPublicBaseSub.class)
- .addFeatureSplit(Feature.class)
- .addKeepRules("-keep class " + Feature.class.getTypeName() + " { void " + name + "(); }")
+ .addFeatureSplit(Feature.class, FeatureSub.class)
+ .addKeepRules(
+ "-keep class "
+ + main.getHolderClass().getTypeName()
+ + " { void "
+ + main.getMethodName()
+ + "(); }")
.enableIsolatedSplits(enableIsolatedSplits)
.setMinApi(parameters)
.compileWithExpectedDiagnostics(enableIsolatedSplits ? inspector : this::assertNoMessages);
@@ -121,6 +138,10 @@
static class NonPublicBase {
+ protected static void protectedMethod() {
+ // Intentionally empty.
+ }
+
public static void publicMethod() {
// Intentionally empty.
}
@@ -136,6 +157,10 @@
public static class Feature {
+ public static void testPublicToProtected() {
+ PublicBaseSub.protectedMethod();
+ }
+
public static void testPublicToPublic() {
PublicBaseSub.publicMethod();
}
@@ -148,4 +173,11 @@
NonPublicBaseSub.publicMethod();
}
}
+
+ public static class FeatureSub extends NonPublicBase {
+
+ public static void testPublicToProtected() {
+ PublicBaseSub.protectedMethod();
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
index f983650..a92c140 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -15,12 +15,12 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.classmerging.ClassMergerMode;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.BooleanUtils;
@@ -105,7 +105,7 @@
private void fixInliningNullabilityClass(
DexItemFactory dexItemFactory,
HorizontallyMergedClasses horizontallyMergedClasses,
- HorizontalClassMerger.Mode mode) {
+ ClassMergerMode mode) {
DexType originalType =
dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor("inlining.Nullability"));
nullabilityClass =
diff --git a/src/test/java/com/android/tools/r8/keepanno/ArrayPatternsTest.java b/src/test/java/com/android/tools/r8/keepanno/ArrayPatternsTest.java
new file mode 100644
index 0000000..a7cf54f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/ArrayPatternsTest.java
@@ -0,0 +1,136 @@
+// 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;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.keepanno.annotations.TypePattern;
+import com.android.tools.r8.keepanno.annotations.UsedByReflection;
+import com.android.tools.r8.keepanno.annotations.UsesReflection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.Arrays;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ArrayPatternsTest extends TestBase {
+
+ static final String EXPECTED =
+ StringUtils.lines("int[] [1, 2, 3]", "int[][] [[42]]", "Integer[][][] [[[333]]]");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build();
+ }
+
+ public ArrayPatternsTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(getInputClasses())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .enableExperimentalKeepAnnotations()
+ .addProgramClasses(getInputClasses())
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkOutput);
+ }
+
+ public List<Class<?>> getInputClasses() {
+ return ImmutableList.of(TestClass.class, A.class, B.class);
+ }
+
+ private void checkOutput(CodeInspector inspector) {
+ assertThat(inspector.clazz(B.class), isPresentAndRenamed());
+ assertThat(inspector.clazz(B.class).method("void", "bar"), isAbsent());
+ assertThat(inspector.clazz(B.class).method("void", "bar", "int[]"), isPresent());
+ assertThat(inspector.clazz(B.class).method("void", "bar", "int[][]"), isPresent());
+ assertThat(inspector.clazz(B.class).method("void", "bar", "int[][][]"), isAbsent());
+ assertThat(
+ inspector.clazz(B.class).method("void", "bar", "java.lang.Integer[][][]"), isPresent());
+ }
+
+ static class A {
+
+ @UsesReflection({
+ @KeepTarget(
+ classConstant = B.class,
+ methodName = "bar",
+ methodParameterTypePatterns = {@TypePattern(constant = int[].class)}),
+ @KeepTarget(
+ classConstant = B.class,
+ methodName = "bar",
+ methodParameters = {"int[][]"}),
+ @KeepTarget(
+ classConstant = B.class,
+ methodName = "bar",
+ methodParameterTypePatterns = {@TypePattern(name = "java.lang.Integer[][][]")}),
+ })
+ public void foo() throws Exception {
+ // Invoke the first and second method.
+ B.class.getDeclaredMethod("bar", int[].class).invoke(null, (Object) new int[] {1, 2, 3});
+ B.class
+ .getDeclaredMethod("bar", int[][].class)
+ .invoke(null, (Object) new int[][] {new int[] {42}});
+ B.class
+ .getDeclaredMethod("bar", Integer[][][].class)
+ .invoke(null, (Object) new Integer[][][] {new Integer[][] {new Integer[] {333}}});
+ }
+ }
+
+ static class B {
+ public static void bar() {
+ throw new RuntimeException("UNUSED");
+ }
+
+ public static void bar(int[] value) {
+ System.out.println("int[] " + Arrays.toString(value));
+ }
+
+ public static void bar(int[][] value) {
+ System.out.println("int[][] " + Arrays.deepToString(value));
+ }
+
+ public static void bar(int[][][] value) {
+ throw new RuntimeException("UNUSED");
+ }
+
+ public static void bar(Integer[][][] value) {
+ System.out.println("Integer[][][] " + Arrays.deepToString(value));
+ }
+ }
+
+ static class TestClass {
+
+ @UsedByReflection(kind = KeepItemKind.CLASS_AND_METHODS)
+ public static void main(String[] args) throws Exception {
+ new A().foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepInvalidForApiTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepInvalidForApiTest.java
index 88d7afa..af2087b 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepInvalidForApiTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepInvalidForApiTest.java
@@ -15,8 +15,8 @@
import com.android.tools.r8.keepanno.annotations.KeepForApi;
import com.android.tools.r8.keepanno.annotations.MemberAccessFlags;
import com.android.tools.r8.keepanno.asm.KeepEdgeReader;
+import com.android.tools.r8.keepanno.ast.KeepAnnotationParserException;
import com.android.tools.r8.keepanno.ast.KeepDeclaration;
-import com.android.tools.r8.keepanno.ast.KeepEdgeException;
import com.android.tools.r8.keepanno.keeprules.KeepRuleExtractor;
import java.io.IOException;
import java.util.ArrayList;
@@ -51,7 +51,7 @@
private void assertThrowsWith(ThrowingRunnable fn, Matcher<String> matcher) {
try {
fn.run();
- } catch (KeepEdgeException e) {
+ } catch (KeepAnnotationParserException e) {
assertThat(e.getMessage(), matcher);
return;
} catch (Throwable e) {
@@ -66,8 +66,9 @@
() -> extractRuleForClass(RefineMemberAccess.class),
allOf(
containsString("Unexpected array"),
- containsString("@KeepForApi"),
- containsString("memberAccess")));
+ containsString("memberAccess"),
+ containsString("at annotation: @KeepForApi"),
+ containsString("at method: void main")));
}
static class RefineMemberAccess {
@@ -84,8 +85,9 @@
() -> extractRuleForClass(RefineMethodName.class),
allOf(
containsString("Unexpected value"),
- containsString("@KeepForApi"),
- containsString("methodName")));
+ containsString("methodName"),
+ containsString("at annotation: @KeepForApi"),
+ containsString("at method: void main")));
}
static class RefineMethodName {
@@ -102,8 +104,9 @@
() -> extractRuleForClass(RefineFieldName.class),
allOf(
containsString("Unexpected value"),
- containsString("@KeepForApi"),
- containsString("fieldName")));
+ containsString("fieldName"),
+ containsString("at annotation: @KeepForApi"),
+ containsString("at method: void main")));
}
static class RefineFieldName {
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepInvalidTargetTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepInvalidTargetTest.java
index eb5791a..b3baa2e 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepInvalidTargetTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepInvalidTargetTest.java
@@ -14,10 +14,11 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.keepanno.annotations.KeepOption;
import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.keepanno.annotations.StringPattern;
import com.android.tools.r8.keepanno.annotations.UsesReflection;
import com.android.tools.r8.keepanno.asm.KeepEdgeReader;
+import com.android.tools.r8.keepanno.ast.KeepAnnotationParserException;
import com.android.tools.r8.keepanno.ast.KeepDeclaration;
-import com.android.tools.r8.keepanno.ast.KeepEdgeException;
import com.android.tools.r8.keepanno.keeprules.KeepRuleExtractor;
import java.io.IOException;
import java.util.ArrayList;
@@ -52,7 +53,7 @@
private void assertThrowsWith(ThrowingRunnable fn, Matcher<String> matcher) {
try {
fn.run();
- } catch (KeepEdgeException e) {
+ } catch (KeepAnnotationParserException e) {
assertThat(e.getMessage(), matcher);
return;
} catch (Throwable e) {
@@ -66,9 +67,10 @@
assertThrowsWith(
() -> extractRuleForClass(MultipleClassDeclarations.class),
allOf(
- containsString("Multiple declarations"),
+ containsString("Multiple properties"),
containsString("className"),
- containsString("classConstant")));
+ containsString("classConstant"),
+ containsString("at property-group: class-name")));
}
static class MultipleClassDeclarations {
@@ -83,13 +85,15 @@
public void testInvalidClassDeclWithBinding() {
assertThrowsWith(
() -> extractRuleForClass(BindingAndClassDeclarations.class),
- allOf(containsString("class binding"), containsString("class patterns")));
+ allOf(
+ containsString("class binding"),
+ containsString("class patterns"),
+ containsString("at property-group: class")));
}
static class BindingAndClassDeclarations {
- // Both properties are using the "default" value of an empty string, but should still fail.
- @UsesReflection({@KeepTarget(classFromBinding = "", className = "")})
+ @UsesReflection({@KeepTarget(classFromBinding = "BINDING", className = "CLASS")})
public static void main(String[] args) {
System.out.println("Hello, world");
}
@@ -100,9 +104,12 @@
assertThrowsWith(
() -> extractRuleForClass(MultipleExtendsDeclarations.class),
allOf(
- containsString("Multiple declarations"),
+ containsString("Multiple properties"),
containsString("extendsClassName"),
- containsString("extendsClassConstant")));
+ containsString("extendsClassConstant"),
+ containsString("at property-group: instance-of"),
+ containsString("at annotation: @UsesReflection"),
+ containsString("at method: void main")));
}
static class MultipleExtendsDeclarations {
@@ -120,7 +127,10 @@
public void testInvalidMemberDecl() {
assertThrowsWith(
() -> extractRuleForClass(MultipleMemberDeclarations.class),
- allOf(containsString("field"), containsString("method")));
+ allOf(
+ containsString("field"),
+ containsString("method"),
+ containsString("at property-group: member")));
}
static class MultipleMemberDeclarations {
@@ -135,7 +145,11 @@
public void testInvalidOptionsDecl() {
assertThrowsWith(
() -> extractRuleForClass(MultipleOptionDeclarations.class),
- allOf(containsString("options"), containsString("allow"), containsString("disallow")));
+ allOf(
+ containsString("Multiple properties"),
+ containsString("allow"),
+ containsString("disallow"),
+ containsString("at property-group: constraints")));
}
static class MultipleOptionDeclarations {
@@ -150,6 +164,28 @@
}
}
+ @Test
+ public void testStringPattern() {
+ assertThrowsWith(
+ () -> extractRuleForClass(StringPatternWithExactAndPrefix.class),
+ allOf(
+ containsString("Cannot specify both"),
+ containsString("exact"),
+ containsString("prefix"),
+ containsString("at property: methodNamePattern")));
+ }
+
+ static class StringPatternWithExactAndPrefix {
+
+ @UsesReflection(
+ @KeepTarget(
+ classConstant = A.class,
+ methodNamePattern = @StringPattern(exact = "foo", startsWith = "f")))
+ public static void main(String[] args) {
+ System.out.println("Hello, world");
+ }
+ }
+
static class A {
// just a target.
}
diff --git a/src/test/java/com/android/tools/r8/keepanno/MethodNameStringPatternsTest.java b/src/test/java/com/android/tools/r8/keepanno/MethodNameStringPatternsTest.java
new file mode 100644
index 0000000..2444ccf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/MethodNameStringPatternsTest.java
@@ -0,0 +1,140 @@
+// 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;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.keepanno.annotations.KeepItemKind;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.keepanno.annotations.StringPattern;
+import com.android.tools.r8.keepanno.annotations.UsedByReflection;
+import com.android.tools.r8.keepanno.annotations.UsesReflection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.lang.reflect.Method;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class MethodNameStringPatternsTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("1");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build();
+ }
+
+ public MethodNameStringPatternsTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(getInputClasses())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .enableExperimentalKeepAnnotations()
+ .addProgramClasses(getInputClasses())
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkOutput);
+ }
+
+ public List<Class<?>> getInputClasses() {
+ return ImmutableList.of(TestClass.class, A.class, B.class);
+ }
+
+ private void checkOutput(CodeInspector inspector) {
+ assertThat(inspector.clazz(B.class), isPresentAndRenamed());
+ assertThat(inspector.clazz(B.class).method("void", "bar"), isAbsent());
+ assertThat(inspector.clazz(B.class).method("int", "getMyI"), isPresent());
+ assertThat(inspector.clazz(B.class).method("void", "setMyI", "int"), isPresent());
+ assertThat(inspector.clazz(B.class).method("java.lang.String", "getMyS"), isAbsent());
+ assertThat(inspector.clazz(B.class).method("void", "setMyS", "java.lang.String"), isAbsent());
+ }
+
+ static class A {
+
+ @UsesReflection({
+ @KeepTarget(
+ classConstant = B.class,
+ methodNamePattern = @StringPattern(startsWith = "get", endsWith = "I")),
+ @KeepTarget(
+ classConstant = B.class,
+ methodNamePattern = @StringPattern(startsWith = "set", endsWith = "I")),
+ })
+ public void foo() throws Exception {
+ int counter = 1;
+ for (Method method : B.class.getDeclaredMethods()) {
+ String name = method.getName();
+ if (name.startsWith("set")) {
+ if (name.endsWith("I")) {
+ method.invoke(null, counter++);
+ }
+ }
+ }
+ for (Method method : B.class.getDeclaredMethods()) {
+ String name = method.getName();
+ if (name.startsWith("get")) {
+ if (name.endsWith("I")) {
+ System.out.println(method.invoke(null));
+ }
+ }
+ }
+ }
+ }
+
+ static class B {
+ private static int i;
+ private static String s;
+
+ public static int getMyI() {
+ return i;
+ }
+
+ public static String getMyS() {
+ return s;
+ }
+
+ public static void setMyI(int i) {
+ B.i = i;
+ }
+
+ public static void setMyS(String s) {
+ B.s = s;
+ }
+
+ public static void bar() {
+ throw new RuntimeException("UNUSED");
+ }
+ }
+
+ static class TestClass {
+
+ @UsedByReflection(kind = KeepItemKind.CLASS_AND_METHODS)
+ public static void main(String[] args) throws Exception {
+ new A().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 f0ea424..8a83cf9 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,7 @@
.build();
assertEquals(
StringUtils.unixLines(
- "-keep class * { void finalize(); }", "-keepclassmembers class * { *; }"),
+ "-keep class ** { void finalize(); }", "-keepclassmembers class ** { *; }"),
extract(edge));
}
@@ -83,8 +83,8 @@
// targeted members.
assertEquals(
StringUtils.unixLines(
- "-keep,allow" + allows + " class * { void finalize(); }",
- "-keepclassmembers,allow" + allows + " class * { *; }"),
+ "-keep,allow" + allows + " class ** { void finalize(); }",
+ "-keepclassmembers,allow" + allows + " class ** { *; }"),
extract(edge));
}
@@ -110,8 +110,8 @@
// Allow is just the ordered list of options.
assertEquals(
StringUtils.unixLines(
- "-keep,allowshrinking,allowobfuscation class * { void finalize(); }",
- "-keepclassmembers,allowshrinking,allowobfuscation class * { *; }"),
+ "-keep,allowshrinking,allowobfuscation class ** { void finalize(); }",
+ "-keepclassmembers,allowshrinking,allowobfuscation class ** { *; }"),
extract(edge));
}
diff --git a/src/test/java/com/android/tools/r8/keepanno/classpatterns/ClassNamePatternsTest.java b/src/test/java/com/android/tools/r8/keepanno/classpatterns/ClassNamePatternsTest.java
index 3468b4c..699ddd6 100644
--- a/src/test/java/com/android/tools/r8/keepanno/classpatterns/ClassNamePatternsTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/classpatterns/ClassNamePatternsTest.java
@@ -30,7 +30,19 @@
static final Class<?> A2 = com.android.tools.r8.keepanno.classpatterns.pkg2.A.class;
static final Class<?> B2 = com.android.tools.r8.keepanno.classpatterns.pkg2.B.class;
- static final String EXPECTED_ALL = StringUtils.lines("pkg1.A", "pkg1.B", "pkg2.A", "pkg2.B");
+ static final String EXPECTED_ALL =
+ StringUtils.lines(
+ "pkg1.A",
+ "pkg1.A: pkg1.A",
+ "pkg1.B",
+ "pkg1.B: pkg1.B",
+ "pkg2.A",
+ "pkg2.A: pkg2.A",
+ "pkg2.B",
+ "pkg2.B: pkg2.B");
+
+ static final String EXPECTED_ALL_NON_VOID =
+ StringUtils.lines("pkg1.A", "pkg1.B", "pkg2.A", "pkg2.B");
static final String EXPECTED_PKG = StringUtils.lines("pkg1.A", "pkg1.B");
static final String EXPECTED_NAME = StringUtils.lines("pkg1.B", "pkg2.B");
static final String EXPECTED_SINGLE = StringUtils.lines("pkg2.A");
@@ -71,6 +83,11 @@
}
@Test
+ public void testAllNoVoidR8() throws Exception {
+ runTestR8(TestAllNoVoid.class, EXPECTED_ALL_NON_VOID);
+ }
+
+ @Test
public void testPkgR8() throws Exception {
runTestR8(TestPkg.class, EXPECTED_PKG);
}
@@ -85,6 +102,11 @@
runTestR8(TestSingle.class, EXPECTED_SINGLE);
}
+ @Test
+ public void testSingleNonExactR8() throws Exception {
+ runTestR8(TestSingleWithNonExactReturnTypeClassPattern.class, EXPECTED_SINGLE);
+ }
+
public List<Class<?>> getBaseInputClasses() {
return ImmutableList.of(Util.class, A1, B1, A2, B2);
}
@@ -97,6 +119,7 @@
try {
Class<?> clazz = Class.forName(type);
System.out.println(clazz.getDeclaredMethod("foo").invoke(null));
+ clazz.getDeclaredMethod("foo", String.class).invoke(null, pkg + "." + name);
} catch (ClassNotFoundException ignored) {
} catch (IllegalAccessException ignored) {
} catch (InvocationTargetException ignored) {
@@ -115,7 +138,8 @@
// The empty class pattern is equivalent to "any class".
classNamePattern = @ClassNamePattern(),
methodName = "foo",
- methodReturnTypeConstant = String.class)
+ // The empty type pattern used in a return-type context will match 'void'.
+ methodReturnTypePattern = @TypePattern())
})
public void foo() throws Exception {
Util.lookupClassesAndInvokeMethods();
@@ -127,6 +151,25 @@
}
}
+ static class TestAllNoVoid {
+
+ @UsesReflection({
+ @KeepTarget(
+ kind = KeepItemKind.CLASS_AND_METHODS,
+ methodName = "foo",
+ // Matching any class does not include 'void'.
+ methodReturnTypePattern = @TypePattern(classNamePattern = @ClassNamePattern()))
+ })
+ public void foo() throws Exception {
+ Util.lookupClassesAndInvokeMethods();
+ }
+
+ @UsedByReflection(kind = KeepItemKind.CLASS_AND_METHODS)
+ public static void main(String[] args) throws Exception {
+ new TestAllNoVoid().foo();
+ }
+ }
+
static class TestPkg {
@UsesReflection({
@@ -189,4 +232,26 @@
new TestSingle().foo();
}
}
+
+ static class TestSingleWithNonExactReturnTypeClassPattern {
+
+ @UsesReflection(
+ @KeepTarget(
+ kind = KeepItemKind.CLASS_AND_METHODS,
+ classNamePattern =
+ @ClassNamePattern(
+ simpleName = "A",
+ packageName = "com.android.tools.r8.keepanno.classpatterns.pkg2"),
+ methodName = "foo",
+ methodReturnTypePattern =
+ @TypePattern(classNamePattern = @ClassNamePattern(simpleName = "String"))))
+ public void foo() throws Exception {
+ Util.lookupClassesAndInvokeMethods();
+ }
+
+ @UsedByReflection(kind = KeepItemKind.CLASS_AND_METHODS)
+ public static void main(String[] args) throws Exception {
+ new TestSingleWithNonExactReturnTypeClassPattern().foo();
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/keepanno/classpatterns/pkg1/A.java b/src/test/java/com/android/tools/r8/keepanno/classpatterns/pkg1/A.java
index eab0e5f..377601c 100644
--- a/src/test/java/com/android/tools/r8/keepanno/classpatterns/pkg1/A.java
+++ b/src/test/java/com/android/tools/r8/keepanno/classpatterns/pkg1/A.java
@@ -9,4 +9,8 @@
public static String foo() {
return "pkg1.A";
}
+
+ public static void foo(String arg) {
+ System.out.println("pkg1.A: " + arg);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/keepanno/classpatterns/pkg1/B.java b/src/test/java/com/android/tools/r8/keepanno/classpatterns/pkg1/B.java
index edeb6b8..b37f208 100644
--- a/src/test/java/com/android/tools/r8/keepanno/classpatterns/pkg1/B.java
+++ b/src/test/java/com/android/tools/r8/keepanno/classpatterns/pkg1/B.java
@@ -9,4 +9,8 @@
public static String foo() {
return "pkg1.B";
}
+
+ public static void foo(String arg) {
+ System.out.println("pkg1.B: " + arg);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/keepanno/classpatterns/pkg2/A.java b/src/test/java/com/android/tools/r8/keepanno/classpatterns/pkg2/A.java
index ca0667e..1670103 100644
--- a/src/test/java/com/android/tools/r8/keepanno/classpatterns/pkg2/A.java
+++ b/src/test/java/com/android/tools/r8/keepanno/classpatterns/pkg2/A.java
@@ -9,4 +9,8 @@
public static String foo() {
return "pkg2.A";
}
+
+ public static void foo(String arg) {
+ System.out.println("pkg2.A: " + arg);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/keepanno/classpatterns/pkg2/B.java b/src/test/java/com/android/tools/r8/keepanno/classpatterns/pkg2/B.java
index c98e746..ab18f98 100644
--- a/src/test/java/com/android/tools/r8/keepanno/classpatterns/pkg2/B.java
+++ b/src/test/java/com/android/tools/r8/keepanno/classpatterns/pkg2/B.java
@@ -9,4 +9,8 @@
public static String foo() {
return "pkg2.B";
}
+
+ public static void foo(String arg) {
+ System.out.println("pkg2.B: " + arg);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java b/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
index 312e8bb..0ab89fa 100644
--- a/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
+++ b/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.keepanno.annotations.KeepTarget;
import com.android.tools.r8.keepanno.annotations.MemberAccessFlags;
import com.android.tools.r8.keepanno.annotations.MethodAccessFlags;
+import com.android.tools.r8.keepanno.annotations.StringPattern;
import com.android.tools.r8.keepanno.annotations.TypePattern;
import com.android.tools.r8.keepanno.annotations.UsedByNative;
import com.android.tools.r8.keepanno.annotations.UsedByReflection;
@@ -53,6 +54,8 @@
Generator.run();
}
+ private static final String DEFAULT_INVALID_STRING_PATTERN =
+ "@" + simpleName(StringPattern.class) + "(exact = \"\")";
private static final String DEFAULT_INVALID_TYPE_PATTERN =
"@" + simpleName(TypePattern.class) + "(name = \"\")";
private static final String DEFAULT_INVALID_CLASS_NAME_PATTERN =
@@ -151,10 +154,17 @@
final List<String> footers = new ArrayList<>();
final LinkedHashMap<String, Group> mutuallyExclusiveGroups = new LinkedHashMap<>();
+ boolean mutuallyExclusiveWithOtherGroups = false;
+
private Group(String name) {
this.name = name;
}
+ Group allowMutuallyExclusiveWithOtherGroups() {
+ mutuallyExclusiveWithOtherGroups = true;
+ return this;
+ }
+
Group addMember(GroupMember member) {
members.add(member);
return this;
@@ -199,6 +209,18 @@
}
void generateConstants(Generator generator) {
+ if (mutuallyExclusiveWithOtherGroups || members.size() > 1) {
+ StringBuilder camelCaseName = new StringBuilder();
+ for (int i = 0; i < name.length(); i++) {
+ char c = name.charAt(i);
+ if (c == '-') {
+ c = Character.toUpperCase(name.charAt(++i));
+ }
+ camelCaseName.append(c);
+ }
+ generator.println(
+ "public static final String " + camelCaseName + "Group = " + quote(name) + ";");
+ }
for (GroupMember member : members) {
member.generateConstants(generator);
}
@@ -206,6 +228,7 @@
public void addMutuallyExclusiveGroups(Group... groups) {
for (Group group : groups) {
+ assert mutuallyExclusiveWithOtherGroups || group.mutuallyExclusiveWithOtherGroups;
mutuallyExclusiveGroups.computeIfAbsent(
group.name,
k -> {
@@ -341,6 +364,38 @@
.defaultArrayEmpty(KeepTarget.class));
}
+ private Group stringPatternExactGroup() {
+ return new Group("string-exact-pattern")
+ .allowMutuallyExclusiveWithOtherGroups()
+ .addMember(
+ new GroupMember("exact")
+ .setDocTitle("Exact string content.")
+ .addParagraph("For example, {@code \"foo\"} or {@code \"java.lang.String\"}.")
+ .defaultEmptyString());
+ }
+
+ private Group stringPatternPrefixGroup() {
+ return new Group("string-prefix-pattern")
+ .addMember(
+ new GroupMember("startsWith")
+ .setDocTitle("Matches strings beginning with the given prefix.")
+ .addParagraph(
+ "For example, {@code \"get\"} to match strings such as {@code"
+ + " \"getMyValue\"}.")
+ .defaultEmptyString());
+ }
+
+ private Group stringPatternSuffixGroup() {
+ return new Group("string-suffix-pattern")
+ .addMember(
+ new GroupMember("endsWith")
+ .setDocTitle("Matches strings ending with the given suffix.")
+ .addParagraph(
+ "For example, {@code \"Setter\"} to match strings such as {@code"
+ + " \"myValueSetter\"}.")
+ .defaultEmptyString());
+ }
+
private Group typePatternGroup() {
return new Group("type-pattern")
.addMember(
@@ -502,6 +557,7 @@
private Group createClassBindingGroup() {
return new Group(CLASS_GROUP)
+ .allowMutuallyExclusiveWithOtherGroups()
.addMember(classFromBinding())
.addDocFooterParagraph("If none are specified the default is to match any class.");
}
@@ -627,6 +683,7 @@
private Group createMemberBindingGroup() {
return new Group("member")
+ .allowMutuallyExclusiveWithOtherGroups()
.addMember(
new GroupMember("memberFromBinding")
.setDocTitle("Define the member pattern in full by a reference to a binding.")
@@ -689,7 +746,14 @@
.addParagraph(getMutuallyExclusiveForMethodProperties())
.addParagraph(getMethodDefaultDoc("any method name"))
.setDocReturn("The exact method name of the method.")
- .defaultEmptyString());
+ .defaultEmptyString())
+ .addMember(
+ new GroupMember("methodNamePattern")
+ .setDocTitle("Define the method-name pattern by a string pattern.")
+ .addParagraph(getMutuallyExclusiveForMethodProperties())
+ .addParagraph(getMethodDefaultDoc("any method name"))
+ .setDocReturn("The string pattern of the method name.")
+ .defaultValue(StringPattern.class, DEFAULT_INVALID_STRING_PATTERN));
}
private Group createMethodReturnTypeGroup() {
@@ -810,36 +874,77 @@
}
// Member binding properties.
+ Group memberBindingGroup = null;
if (includeMemberBinding) {
- createMemberBindingGroup().generate(this);
+ memberBindingGroup = createMemberBindingGroup();
+ memberBindingGroup.generate(this);
println();
}
// The remaining member properties.
- generateMemberPropertiesNoBinding();
+ internalGenerateMemberPropertiesNoBinding(memberBindingGroup);
+ }
+
+ private Group maybeLink(Group group, Group maybeExclusiveGroup) {
+ if (maybeExclusiveGroup != null) {
+ maybeExclusiveGroup.addMutuallyExclusiveGroups(group);
+ }
+ return group;
}
private void generateMemberPropertiesNoBinding() {
+ internalGenerateMemberPropertiesNoBinding(null);
+ }
+
+ private void internalGenerateMemberPropertiesNoBinding(Group memberBindingGroup) {
// General member properties.
- createMemberAccessGroup().generate(this);
+ maybeLink(createMemberAccessGroup(), memberBindingGroup).generate(this);
println();
// Method properties.
- createMethodAccessGroup().generate(this);
+ maybeLink(createMethodAccessGroup(), memberBindingGroup).generate(this);
println();
- createMethodNameGroup().generate(this);
+ maybeLink(createMethodNameGroup(), memberBindingGroup).generate(this);
println();
- createMethodReturnTypeGroup().generate(this);
+ maybeLink(createMethodReturnTypeGroup(), memberBindingGroup).generate(this);
println();
- createMethodParametersGroup().generate(this);
+ maybeLink(createMethodParametersGroup(), memberBindingGroup).generate(this);
println();
// Field properties.
- createFieldAccessGroup().generate(this);
+ maybeLink(createFieldAccessGroup(), memberBindingGroup).generate(this);
println();
- createFieldNameGroup().generate(this);
+ maybeLink(createFieldNameGroup(), memberBindingGroup).generate(this);
println();
- createFieldTypeGroup().generate(this);
+ maybeLink(createFieldTypeGroup(), memberBindingGroup).generate(this);
+ }
+
+ private void generateStringPattern() {
+ printCopyRight(2024);
+ printPackage("annotations");
+ printImports(ANNOTATION_IMPORTS);
+ DocPrinter.printer()
+ .setDocTitle("A pattern structure for matching strings.")
+ .addParagraph("If no properties are set, the default pattern matches any string.")
+ .printDoc(this::println);
+ println("@Target(ElementType.ANNOTATION_TYPE)");
+ println("@Retention(RetentionPolicy.CLASS)");
+ println("public @interface " + simpleName(StringPattern.class) + " {");
+ println();
+ withIndent(
+ () -> {
+ Group exactGroup = stringPatternExactGroup();
+ Group prefixGroup = stringPatternPrefixGroup();
+ Group suffixGroup = stringPatternSuffixGroup();
+ exactGroup.addMutuallyExclusiveGroups(prefixGroup, suffixGroup);
+ exactGroup.generate(this);
+ println();
+ prefixGroup.generate(this);
+ println();
+ suffixGroup.generate(this);
+ });
+ println();
+ println("}");
}
private void generateTypePattern() {
@@ -1213,6 +1318,7 @@
generateMethodAccessConstants();
generateFieldAccessConstants();
+ generateStringPatternConstants();
generateTypePatternConstants();
generateClassNamePatternConstants();
});
@@ -1482,6 +1588,19 @@
println();
}
+ private void generateStringPatternConstants() {
+ println("public static final class StringPattern {");
+ withIndent(
+ () -> {
+ generateAnnotationConstants(StringPattern.class);
+ stringPatternExactGroup().generateConstants(this);
+ stringPatternPrefixGroup().generateConstants(this);
+ stringPatternSuffixGroup().generateConstants(this);
+ });
+ println("}");
+ println();
+ }
+
private void generateTypePatternConstants() {
println("public static final class TypePattern {");
withIndent(
@@ -1530,6 +1649,7 @@
writeFile(source(astPkg, AnnotationConstants.class), Generator::generateConstants);
Path annoPkg = Paths.get("src/keepanno/java/com/android/tools/r8/keepanno/annotations");
+ writeFile(source(annoPkg, StringPattern.class), Generator::generateStringPattern);
writeFile(source(annoPkg, TypePattern.class), Generator::generateTypePattern);
writeFile(source(annoPkg, ClassNamePattern.class), Generator::generateClassNamePattern);
writeFile(source(annoPkg, KeepBinding.class), Generator::generateKeepBinding);
diff --git a/tools/compiledump.py b/tools/compiledump.py
index cc12d2a..5c926b3 100755
--- a/tools/compiledump.py
+++ b/tools/compiledump.py
@@ -102,14 +102,13 @@
'Enable additional test assertions when running the compiler (default disabled)',
default=False,
action='store_true')
- parser.add_argument(
- '--java-opts',
- '--java-opts',
- '-J',
- metavar='<JVM argument(s)>',
- default=[],
- action='append',
- help='Additional options to pass to JVM invocation')
+ parser.add_argument('--java-opts',
+ '--java-opts',
+ '-J',
+ metavar='<JVM argument(s)>',
+ default=[],
+ action='append',
+ help='Additional options to pass to JVM invocation')
parser.add_argument('--classfile',
help='Run with classfile output',
default=False,
@@ -158,15 +157,14 @@
parser.add_argument(
'--ignore-features',
help="Don't split into features when features are present."
- ' Instead include feature code in main app output.'
- ' This is always the case when compiler is d8.',
+ ' Instead include feature code in main app output.'
+ ' This is always the case when compiler is d8.',
default=False,
action='store_true')
- parser.add_argument(
- '--no-build',
- help="Don't build when using --version main",
- default=False,
- action='store_true')
+ parser.add_argument('--no-build',
+ help="Don't build when using --version main",
+ default=False,
+ action='store_true')
return parser
@@ -319,6 +317,14 @@
return compiler
+def determine_isolated_splits(build_properties, feature_jars):
+ if feature_jars and 'isolated-splits' in build_properties:
+ isolated_splits = build_properties.get('isolated-splits')
+ assert isolated_splits == 'true' or isolated_splits == 'false'
+ return isolated_splits == 'true'
+ return None
+
+
def determine_trace_references_commands(build_properties, output):
trace_ref_consumer = build_properties.get('trace_references_consumer')
if trace_ref_consumer == 'com.android.tools.r8.tracereferences.TraceReferencesCheckConsumer':
@@ -432,9 +438,8 @@
nolib = args.nolib
if version == 'main':
if not args.no_build:
- gradle.RunGradle(
- [utils.GRADLE_TASK_R8] if nolib else [utils.GRADLE_TASK_R8LIB]
- )
+ gradle.RunGradle(
+ [utils.GRADLE_TASK_R8] if nolib else [utils.GRADLE_TASK_R8LIB])
return utils.R8_JAR if nolib else utils.R8LIB_JAR
if version == 'source':
return '%s:%s' % (utils.BUILD_JAVA_MAIN_DIR, utils.ALL_DEPS_JAR)
@@ -576,6 +581,9 @@
cmd.append('-ea')
if args.enable_test_assertions:
cmd.append('-Dcom.android.tools.r8.enableTestAssertions=1')
+ feature_jars = dump.feature_jars()
+ if determine_isolated_splits(build_properties, feature_jars):
+ cmd.append('-Dcom.android.tools.r8.isolatedSplits=1')
if args.print_times:
cmd.append('-Dcom.android.tools.r8.printtimes=1')
if args.r8_flags:
@@ -609,7 +617,7 @@
cmd.extend(['--output', out])
else:
cmd.extend(['--source', program_jar])
- for feature_jar in dump.feature_jars():
+ for feature_jar in feature_jars:
if not args.ignore_features and compiler != 'd8':
cmd.extend([
'--feature-jar', feature_jar,