Merge commit 'deb99884c39a3814749bc59c4d10eaaac3dffbd4' into dev-release Change-Id: I76528df9229f5657730a8affd903a4fd00517e70
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt index effc693..fe9bd92 100644 --- a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt +++ b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
@@ -356,7 +356,7 @@ const val javassist = "3.29.2-GA" const val junitVersion = "4.13-beta-2" const val kotlinVersion = "1.9.0" - const val kotlinMetadataVersion = "0.9.0" + const val kotlinMetadataVersion = "0.7.0" const val mockito = "2.10.0" const val smaliVersion = "3.0.3" }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/ClassNamePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/ClassNamePattern.java index a175b3d..a2f7bb9 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/ClassNamePattern.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/ClassNamePattern.java
@@ -23,11 +23,48 @@ public @interface ClassNamePattern { /** + * Define the class-name pattern by fully qualified class name. + * + * <p>Mutually exclusive with the following other properties defining class-name: + * + * <ul> + * <li>constant + * <li>simpleName + * <li>packageName + * </ul> + * + * @return The qualified class name that defines the class. + */ + String name() default ""; + + /** + * Define the class-name pattern by reference to a Class constant. + * + * <p>Mutually exclusive with the following other properties defining class-name: + * + * <ul> + * <li>name + * <li>simpleName + * <li>packageName + * </ul> + * + * @return The class-constant that defines the class. + */ + Class<?> constant() default Object.class; + + /** * Exact simple name of the class or interface. * * <p>For example, the simple name of {@code com.example.MyClass} is {@code MyClass}. * * <p>The default matches any simple name. + * + * <p>Mutually exclusive with the following other properties defining class-simple-name: + * + * <ul> + * <li>name + * <li>constant + * </ul> */ String simpleName() default ""; @@ -37,6 +74,13 @@ * <p>For example, the package of {@code com.example.MyClass} is {@code com.example}. * * <p>The default matches any package. + * + * <p>Mutually exclusive with the following other properties defining class-package-name: + * + * <ul> + * <li>name + * <li>constant + * </ul> */ String packageName() default ""; }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/ExtractedKeepAnnotation.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/ExtractedKeepAnnotation.java new file mode 100644 index 0000000..e2e9461 --- /dev/null +++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/ExtractedKeepAnnotation.java
@@ -0,0 +1,34 @@ +// 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.annotations; + +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.CLASS) +@Repeatable(ExtractedKeepAnnotations.class) +public @interface ExtractedKeepAnnotation { + /** + * The version defining this extracted keep annotation. + * + * <p>Note: this version property must be the first property defined. Its content may determine + * the subsequent parsing. + */ + String version(); + + /** + * The context giving rise to this extracted keep annotation. + * + * <p>The context must be a class descriptor, method descriptor or field descriptor. + */ + String context(); + + /** The extracted edge. */ + KeepEdge edge(); + + boolean isCheckRemoved() default false; + + boolean isCheckOptimizedOut() default false; +}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/ExtractedKeepAnnotations.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/ExtractedKeepAnnotations.java index 43a91bb..f6855cc 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/ExtractedKeepAnnotations.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/ExtractedKeepAnnotations.java
@@ -8,24 +8,15 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * Collection of extracted keep annotations. + * + * <p>This annotation is just a collection of the extracted annotations. It is version independent + * and is assumed to never change. Any version specific changes are to be made within the single + * element structure of {@link ExtractedKeepAnnotation}. + */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.CLASS) public @interface ExtractedKeepAnnotations { - /** - * The version of defining this extracted keep annotation. - * - * <p>Note: this version property must be the first property defined. Its content may determine - * the subsequent parsing. - */ - String version(); - - /** - * The context giving rise to this extracted keep annotation. - * - * <p>The context must be a class descriptor, method descriptor or field descriptor. - */ - String context(); - - /** The extracted edges. */ - KeepEdge[] edges(); + ExtractedKeepAnnotation[] value(); }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/InstanceOfPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/InstanceOfPattern.java new file mode 100644 index 0000000..591d750 --- /dev/null +++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/InstanceOfPattern.java
@@ -0,0 +1,35 @@ +// 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 instances of classes and interfaces. + * + * <p>If no properties are set, the default pattern matches any instance. + */ +@Target(ElementType.ANNOTATION_TYPE) +@Retention(RetentionPolicy.CLASS) +public @interface InstanceOfPattern { + + /** + * True if the pattern should include the directly matched classes. + * + * <p>If false, the pattern is exclusive and only matches classes that are strict subclasses of + * the pattern. + */ + boolean inclusive() default true; + + /** Instances of classes matching the class-name pattern. */ + ClassNamePattern classNamePattern() default @ClassNamePattern(simpleName = ""); +}
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 364f3a1..f92ab49 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
@@ -79,6 +79,7 @@ * <li>instanceOfClassNameExclusive * <li>instanceOfClassConstant * <li>instanceOfClassConstantExclusive + * <li>instanceOfPattern * <li>classAnnotatedByClassName * <li>classAnnotatedByClassConstant * <li>classAnnotatedByClassNamePattern @@ -150,6 +151,7 @@ * <li>instanceOfClassNameExclusive * <li>instanceOfClassConstant * <li>instanceOfClassConstantExclusive + * <li>instanceOfPattern * <li>classFromBinding * </ul> * @@ -171,6 +173,7 @@ * <li>instanceOfClassName * <li>instanceOfClassConstant * <li>instanceOfClassConstantExclusive + * <li>instanceOfPattern * <li>classFromBinding * </ul> * @@ -189,6 +192,7 @@ * <li>instanceOfClassName * <li>instanceOfClassNameExclusive * <li>instanceOfClassConstantExclusive + * <li>instanceOfPattern * <li>classFromBinding * </ul> * @@ -210,6 +214,7 @@ * <li>instanceOfClassName * <li>instanceOfClassNameExclusive * <li>instanceOfClassConstant + * <li>instanceOfPattern * <li>classFromBinding * </ul> * @@ -220,6 +225,25 @@ Class<?> instanceOfClassConstantExclusive() default Object.class; /** + * Define the instance-of with a pattern. + * + * <p>Mutually exclusive with the following other properties defining instance-of: + * + * <ul> + * <li>instanceOfClassName + * <li>instanceOfClassNameExclusive + * <li>instanceOfClassConstant + * <li>instanceOfClassConstantExclusive + * <li>classFromBinding + * </ul> + * + * <p>If none are specified the default is to match any class instance. + * + * @return The pattern that defines what instance-of the class must be. + */ + InstanceOfPattern instanceOfPattern() default @InstanceOfPattern(); + + /** * Define the class-annotated-by pattern by fully qualified class name. * * <p>Mutually exclusive with the following other properties defining class-annotated-by:
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 3a96660..65d13d8 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
@@ -41,6 +41,7 @@ * <li>instanceOfClassNameExclusive * <li>instanceOfClassConstant * <li>instanceOfClassConstantExclusive + * <li>instanceOfPattern * <li>classAnnotatedByClassName * <li>classAnnotatedByClassConstant * <li>classAnnotatedByClassNamePattern @@ -112,6 +113,7 @@ * <li>instanceOfClassNameExclusive * <li>instanceOfClassConstant * <li>instanceOfClassConstantExclusive + * <li>instanceOfPattern * <li>classFromBinding * </ul> * @@ -133,6 +135,7 @@ * <li>instanceOfClassName * <li>instanceOfClassConstant * <li>instanceOfClassConstantExclusive + * <li>instanceOfPattern * <li>classFromBinding * </ul> * @@ -151,6 +154,7 @@ * <li>instanceOfClassName * <li>instanceOfClassNameExclusive * <li>instanceOfClassConstantExclusive + * <li>instanceOfPattern * <li>classFromBinding * </ul> * @@ -172,6 +176,7 @@ * <li>instanceOfClassName * <li>instanceOfClassNameExclusive * <li>instanceOfClassConstant + * <li>instanceOfPattern * <li>classFromBinding * </ul> * @@ -182,6 +187,25 @@ Class<?> instanceOfClassConstantExclusive() default Object.class; /** + * Define the instance-of with a pattern. + * + * <p>Mutually exclusive with the following other properties defining instance-of: + * + * <ul> + * <li>instanceOfClassName + * <li>instanceOfClassNameExclusive + * <li>instanceOfClassConstant + * <li>instanceOfClassConstantExclusive + * <li>classFromBinding + * </ul> + * + * <p>If none are specified the default is to match any class instance. + * + * @return The pattern that defines what instance-of the class must be. + */ + InstanceOfPattern instanceOfPattern() default @InstanceOfPattern(); + + /** * Define the class-annotated-by pattern by fully qualified class name. * * <p>Mutually exclusive with the following other properties defining class-annotated-by:
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 20e5492..e7e8c74 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
@@ -138,6 +138,7 @@ * <li>instanceOfClassNameExclusive * <li>instanceOfClassConstant * <li>instanceOfClassConstantExclusive + * <li>instanceOfPattern * <li>classAnnotatedByClassName * <li>classAnnotatedByClassConstant * <li>classAnnotatedByClassNamePattern @@ -209,6 +210,7 @@ * <li>instanceOfClassNameExclusive * <li>instanceOfClassConstant * <li>instanceOfClassConstantExclusive + * <li>instanceOfPattern * <li>classFromBinding * </ul> * @@ -230,6 +232,7 @@ * <li>instanceOfClassName * <li>instanceOfClassConstant * <li>instanceOfClassConstantExclusive + * <li>instanceOfPattern * <li>classFromBinding * </ul> * @@ -248,6 +251,7 @@ * <li>instanceOfClassName * <li>instanceOfClassNameExclusive * <li>instanceOfClassConstantExclusive + * <li>instanceOfPattern * <li>classFromBinding * </ul> * @@ -269,6 +273,7 @@ * <li>instanceOfClassName * <li>instanceOfClassNameExclusive * <li>instanceOfClassConstant + * <li>instanceOfPattern * <li>classFromBinding * </ul> * @@ -279,6 +284,25 @@ Class<?> instanceOfClassConstantExclusive() default Object.class; /** + * Define the instance-of with a pattern. + * + * <p>Mutually exclusive with the following other properties defining instance-of: + * + * <ul> + * <li>instanceOfClassName + * <li>instanceOfClassNameExclusive + * <li>instanceOfClassConstant + * <li>instanceOfClassConstantExclusive + * <li>classFromBinding + * </ul> + * + * <p>If none are specified the default is to match any class instance. + * + * @return The pattern that defines what instance-of the class must be. + */ + InstanceOfPattern instanceOfPattern() default @InstanceOfPattern(); + + /** * Define the class-annotated-by pattern by fully qualified class name. * * <p>Mutually exclusive with the following other properties defining class-annotated-by:
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/BooleanParser.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/BooleanParser.java new file mode 100644 index 0000000..54202fb --- /dev/null +++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/BooleanParser.java
@@ -0,0 +1,30 @@ +// 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.BooleanParser.BooleanProperty; +import com.android.tools.r8.keepanno.ast.ParsingContext; +import java.util.function.Consumer; + +public class BooleanParser extends PropertyParserBase<Boolean, BooleanProperty> { + + public BooleanParser(ParsingContext parsingContext) { + super(parsingContext); + } + + public enum BooleanProperty { + BOOL + } + + @Override + public boolean tryProperty( + BooleanProperty property, String name, Object value, Consumer<Boolean> setValue) { + if (BooleanProperty.BOOL.equals(property) && value instanceof Boolean) { + setValue.accept((Boolean) value); + return true; + } + return false; + } +}
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 6bc17aa..b3fdddb 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
@@ -83,21 +83,35 @@ { AnnotationParsingContext parsingContext = getParsingContext().property(name).annotation(descriptor); + ClassNameParser fullNameParser = new ClassNameParser(parsingContext); PackageNameParser packageParser = new PackageNameParser(parsingContext); - ClassSimpleNameParser nameParser = new ClassSimpleNameParser(parsingContext); + ClassSimpleNameParser simpleNameParser = new ClassSimpleNameParser(parsingContext); + fullNameParser.setProperty(ClassNamePattern.name, ClassNameProperty.NAME); + fullNameParser.setProperty(ClassNamePattern.constant, ClassNameProperty.CONSTANT); packageParser.setProperty(ClassNamePattern.packageName, PackageNameProperty.NAME); - nameParser.setProperty(ClassNamePattern.simpleName, ClassSimpleNameProperty.NAME); + simpleNameParser.setProperty(ClassNamePattern.simpleName, ClassSimpleNameProperty.NAME); return new ParserVisitor( parsingContext, - ImmutableList.of(packageParser, nameParser), - () -> - setValue.accept( - KeepQualifiedClassNamePattern.builder() - .setPackagePattern( - packageParser.getValueOrDefault(KeepPackagePattern.any())) - .setNamePattern( - nameParser.getValueOrDefault(KeepUnqualfiedClassNamePattern.any())) - .build())); + ImmutableList.of(fullNameParser, packageParser, simpleNameParser), + () -> { + if (fullNameParser.isDeclared()) { + if (simpleNameParser.isDeclared() || packageParser.isDeclared()) { + throw parsingContext.error( + "Cannot specify both the full class name and its " + + (simpleNameParser.isDeclared() ? "simple name" : "package")); + } + setValue.accept(fullNameParser.getValue()); + return; + } + setValue.accept( + KeepQualifiedClassNamePattern.builder() + .setPackagePattern( + packageParser.getValueOrDefault(KeepPackagePattern.any())) + .setNamePattern( + simpleNameParser.getValueOrDefault( + KeepUnqualfiedClassNamePattern.any())) + .build()); + }); } default: return null;
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ContextDescriptor.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ContextDescriptor.java new file mode 100644 index 0000000..66bb6d5 --- /dev/null +++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ContextDescriptor.java
@@ -0,0 +1,154 @@ +// 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 static com.android.tools.r8.keepanno.ast.KeepTypePattern.fromDescriptor; + +import com.android.tools.r8.keepanno.ast.KeepClassItemPattern; +import com.android.tools.r8.keepanno.ast.KeepEdgeMetaInfo; +import com.android.tools.r8.keepanno.ast.KeepFieldNamePattern; +import com.android.tools.r8.keepanno.ast.KeepFieldPattern; +import com.android.tools.r8.keepanno.ast.KeepFieldTypePattern; +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; +import com.android.tools.r8.keepanno.ast.KeepMethodParametersPattern; +import com.android.tools.r8.keepanno.ast.KeepMethodPattern; +import com.android.tools.r8.keepanno.ast.KeepMethodReturnTypePattern; +import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern; +import com.android.tools.r8.keepanno.ast.ParsingContext; +import org.objectweb.asm.Type; + +public class ContextDescriptor { + + public static ContextDescriptor parse(String descriptor, ParsingContext parsingContext) { + int classDescriptorEnd = descriptor.indexOf(';') + 1; + if (classDescriptorEnd <= 0) { + throw parsingContext.error("Invalid descriptor: " + descriptor); + } + String classDescriptor = descriptor.substring(0, classDescriptorEnd); + if (classDescriptorEnd == descriptor.length()) { + return new ContextDescriptor(classDescriptor); + } + int memberNameEnd = descriptor.indexOf('(', classDescriptorEnd); + if (memberNameEnd < 0) { + memberNameEnd = descriptor.indexOf(':', classDescriptorEnd); + } + if (memberNameEnd < 0) { + throw parsingContext.error("Invalid descriptor: " + descriptor); + } + String memberName = descriptor.substring(classDescriptorEnd, memberNameEnd); + String memberDescriptor = descriptor.substring(memberNameEnd); + return new ContextDescriptor(classDescriptor, memberName, memberDescriptor); + } + + private final String classDescriptor; + private final String memberName; + private final String memberDescriptor; + + private ContextDescriptor(String classDescriptor) { + this(classDescriptor, null, null); + } + + private ContextDescriptor(String classDescriptor, String memberName, String memberDescriptor) { + this.classDescriptor = classDescriptor; + this.memberName = memberName; + this.memberDescriptor = memberDescriptor; + } + + public boolean isClassContext() { + return memberDescriptor == null; + } + + public boolean isMethodContext() { + return memberDescriptor != null && memberDescriptor.charAt(0) == '('; + } + + public boolean isFieldContext() { + return memberDescriptor != null && memberDescriptor.charAt(0) == ':'; + } + + public String getClassDescriptor() { + return classDescriptor; + } + + public String getMemberName() { + return memberName; + } + + public String getMethodDescriptor() { + assert isMethodContext(); + return memberDescriptor; + } + + public String getFieldType() { + assert isFieldContext(); + return memberDescriptor.substring(1); + } + + public KeepEdgeMetaInfo.Builder applyToMetadata(KeepEdgeMetaInfo.Builder metaInfoBuilder) { + if (isClassContext()) { + metaInfoBuilder.setContextFromClassDescriptor(getClassDescriptor()); + } else if (isMethodContext()) { + metaInfoBuilder.setContextFromMethodDescriptor( + getClassDescriptor(), getMemberName(), getMethodDescriptor()); + } else { + assert isFieldContext(); + metaInfoBuilder.setContextFromFieldDescriptor( + getClassDescriptor(), getMemberName(), getFieldType()); + } + return metaInfoBuilder; + } + + public KeepItemPattern toItemPattern() { + KeepQualifiedClassNamePattern className = + KeepQualifiedClassNamePattern.exactFromDescriptor(getClassDescriptor()); + KeepClassItemPattern classItem = + KeepClassItemPattern.builder().setClassNamePattern(className).build(); + if (isClassContext()) { + return classItem; + } + KeepMemberPattern memberPattern; + if (isMethodContext()) { + memberPattern = createMethodPatternFromDescriptor(); + } else { + assert isFieldContext(); + memberPattern = createFieldPatternFromDescriptor(); + } + return KeepMemberItemPattern.builder() + .setClassReference(classItem.toClassItemReference()) + .setMemberPattern(memberPattern) + .build(); + } + + private KeepFieldPattern createFieldPatternFromDescriptor() { + return KeepFieldPattern.builder() + .setNamePattern(KeepFieldNamePattern.exact(getMemberName())) + .setTypePattern(KeepFieldTypePattern.fromType(fromDescriptor(getFieldType()))) + .build(); + } + + private KeepMethodPattern createMethodPatternFromDescriptor() { + Type methodType = Type.getMethodType(getMethodDescriptor()); + + String returnTypeDescriptor = methodType.getReturnType().getDescriptor(); + KeepMethodReturnTypePattern returnType = + returnTypeDescriptor.equals("V") + ? KeepMethodReturnTypePattern.voidType() + : KeepMethodReturnTypePattern.fromType(fromDescriptor(returnTypeDescriptor)); + + KeepMethodParametersPattern.Builder paramBuilder = KeepMethodParametersPattern.builder(); + for (Type argumentType : methodType.getArgumentTypes()) { + paramBuilder.addParameterTypePattern(fromDescriptor(argumentType.getDescriptor())); + } + + return KeepMethodPattern.builder() + .setNamePattern(KeepMethodNamePattern.exact(getMemberName())) + .setReturnTypePattern(returnType) + .setParametersPattern(paramBuilder.build()) + .build(); + } +}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/InstanceOfParser.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/InstanceOfParser.java index a34915b..73b4905 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/InstanceOfParser.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/InstanceOfParser.java
@@ -4,12 +4,18 @@ package com.android.tools.r8.keepanno.asm; +import com.android.tools.r8.keepanno.asm.BooleanParser.BooleanProperty; +import com.android.tools.r8.keepanno.asm.ClassNameParser.ClassNameProperty; import com.android.tools.r8.keepanno.asm.InstanceOfParser.InstanceOfProperties; +import com.android.tools.r8.keepanno.ast.AnnotationConstants.InstanceOfPattern; import com.android.tools.r8.keepanno.ast.AnnotationConstants.Item; import com.android.tools.r8.keepanno.ast.KeepInstanceOfPattern; import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern; 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; import org.objectweb.asm.Type; public class InstanceOfParser @@ -19,7 +25,8 @@ NAME, NAME_EXCL, CONSTANT, - CONSTANT_EXCL + CONSTANT_EXCL, + PATTERN } public InstanceOfParser(ParsingContext parsingContext) { @@ -64,4 +71,31 @@ return null; } } + + @Override + AnnotationVisitor tryPropertyAnnotation( + InstanceOfProperties property, + String name, + String descriptor, + Consumer<KeepInstanceOfPattern> setValue) { + if (property.equals(InstanceOfProperties.PATTERN)) { + AnnotationParsingContext parsingContext = + getParsingContext().property(name).annotation(descriptor); + BooleanParser inclusiveParser = new BooleanParser(parsingContext); + inclusiveParser.setProperty(InstanceOfPattern.inclusive, BooleanProperty.BOOL); + ClassNameParser classNameParser = new ClassNameParser(parsingContext); + classNameParser.setProperty(InstanceOfPattern.classNamePattern, ClassNameProperty.PATTERN); + return new ParserVisitor( + parsingContext, + ImmutableList.of(inclusiveParser, classNameParser), + () -> + setValue.accept( + KeepInstanceOfPattern.builder() + .setInclusive(inclusiveParser.getValueOrDefault(true)) + .classPattern( + classNameParser.getValueOrDefault(KeepQualifiedClassNamePattern.any())) + .build())); + } + return super.tryPropertyAnnotation(property, name, descriptor, setValue); + } }
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 2d5d253..01d57c7 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
@@ -13,7 +13,8 @@ import com.android.tools.r8.keepanno.ast.AnnotationConstants.Binding; import com.android.tools.r8.keepanno.ast.AnnotationConstants.Condition; import com.android.tools.r8.keepanno.ast.AnnotationConstants.Edge; -import com.android.tools.r8.keepanno.ast.AnnotationConstants.Extracted; +import com.android.tools.r8.keepanno.ast.AnnotationConstants.ExtractedAnnotation; +import com.android.tools.r8.keepanno.ast.AnnotationConstants.ExtractedAnnotations; import com.android.tools.r8.keepanno.ast.AnnotationConstants.FieldAccess; import com.android.tools.r8.keepanno.ast.AnnotationConstants.ForApi; import com.android.tools.r8.keepanno.ast.AnnotationConstants.Item; @@ -89,19 +90,20 @@ public static int ASM_VERSION = ASM9; public static List<KeepDeclaration> readKeepEdges(byte[] classFileBytes) { - return internalReadKeepEdges(classFileBytes, false); + return internalReadKeepEdges(classFileBytes, true, false); } public static List<KeepDeclaration> readExtractedKeepEdges(byte[] classFileBytes) { - return internalReadKeepEdges(classFileBytes, true); + return internalReadKeepEdges(classFileBytes, false, true); } private static List<KeepDeclaration> internalReadKeepEdges( - byte[] classFileBytes, boolean onlyExtracted) { + byte[] classFileBytes, boolean readEmbedded, boolean readExtracted) { ClassReader reader = new ClassReader(classFileBytes); List<KeepDeclaration> declarations = new ArrayList<>(); reader.accept( - new KeepEdgeClassVisitor(onlyExtracted, declarations::add), ClassReader.SKIP_CODE); + new KeepEdgeClassVisitor(readEmbedded, readExtracted, declarations::add), + ClassReader.SKIP_CODE); return declarations; } @@ -193,14 +195,17 @@ } private static class KeepEdgeClassVisitor extends ClassVisitor { - private final boolean onlyExtracted; + private final boolean readEmbedded; + private final boolean readExtracted; private final Parent<KeepDeclaration> parent; private String className; private ClassParsingContext parsingContext; - KeepEdgeClassVisitor(boolean onlyExtracted, Parent<KeepDeclaration> parent) { + KeepEdgeClassVisitor( + boolean readEmbedded, boolean readExtracted, Parent<KeepDeclaration> parent) { super(ASM_VERSION); - this.onlyExtracted = onlyExtracted; + this.readEmbedded = readEmbedded; + this.readExtracted = readExtracted; this.parent = parent; } @@ -231,16 +236,11 @@ if (visible) { return null; } - if (descriptor.equals(Extracted.DESCRIPTOR)) { - if (!onlyExtracted) { - // Annotation reading always ignores extracted edges. - // Note that we may reconsider this if R8 is to support a mixed-mode input. - return null; - } - return new ExtractedAnnotationVisitor(annotationParsingContext(descriptor), parent::accept); + if (readExtracted && descriptor.equals(ExtractedAnnotations.DESCRIPTOR)) { + return new ExtractedAnnotationsVisitor( + annotationParsingContext(descriptor), parent::accept); } - if (onlyExtracted) { - // When reading extracted edges ignore all non-extracted annotations. + if (!readEmbedded) { return null; } if (descriptor.equals(Edge.DESCRIPTOR)) { @@ -294,13 +294,21 @@ @Override public MethodVisitor visitMethod( int access, String name, String descriptor, String signature, String[] exceptions) { - return new KeepEdgeMethodVisitor(parsingContext, parent::accept, className, name, descriptor); + if (readEmbedded) { + return new KeepEdgeMethodVisitor( + parsingContext, parent::accept, className, name, descriptor); + } + return null; } @Override public FieldVisitor visitField( int access, String name, String descriptor, String signature, Object value) { - return new KeepEdgeFieldVisitor(parsingContext, parent::accept, className, name, descriptor); + if (readEmbedded) { + return new KeepEdgeFieldVisitor( + parsingContext, parent::accept, className, name, descriptor); + } + return null; } } @@ -521,12 +529,54 @@ } } + private static class ExtractedAnnotationsVisitor extends AnnotationVisitorBase { + + private final Parent<KeepDeclaration> parent; + private List<KeepDeclaration> declarations = new ArrayList<>(); + + public ExtractedAnnotationsVisitor( + AnnotationParsingContext parsingContext, Parent<KeepDeclaration> parent) { + super(parsingContext); + this.parent = parent; + } + + @Override + public AnnotationVisitor visitArray(String name) { + if (name.equals(ExtractedAnnotations.value)) { + PropertyParsingContext parsingContext = getParsingContext().property(name); + return new AnnotationVisitorBase(parsingContext) { + @Override + public AnnotationVisitor visitAnnotation(String nullName, String descriptor) { + assert nullName == null; + if (descriptor.equals(ExtractedAnnotation.DESCRIPTOR)) { + return new ExtractedAnnotationVisitor( + parsingContext.annotation(descriptor), declarations::add); + } + return super.visitAnnotation(nullName, descriptor); + } + }; + } + return super.visitArray(name); + } + + @Override + public void visitEnd() { + if (declarations.isEmpty()) { + throw new KeepEdgeException("Invalid extracted annotation set, expected non-empty."); + } + declarations.forEach(parent::accept); + super.visitEnd(); + } + } + private static class ExtractedAnnotationVisitor extends AnnotationVisitorBase { private final Parent<KeepDeclaration> parent; - private String context = null; + private ContextDescriptor context = null; private String version = null; - private List<KeepEdgeVisitor> edgeVisitors = new ArrayList<>(); + private KeepEdgeVisitor edgeVisitor = null; + private boolean isCheckRemoved = false; + private boolean isCheckOptimizedOut = false; public ExtractedAnnotationVisitor( AnnotationParsingContext parsingContext, Parent<KeepDeclaration> parent) { @@ -542,61 +592,78 @@ @Override public void visit(String name, Object value) { - if (name.equals(Extracted.version) && value instanceof String) { + if (name.equals(ExtractedAnnotation.version) && value instanceof String) { version = (String) value; return; } - ensureVersion(getParsingContext().property(name)); - if (name.equals(Extracted.context) && value instanceof String) { - context = (String) value; + ParsingContext parsingContext = getParsingContext().property(name); + ensureVersion(parsingContext); + if (name.equals(ExtractedAnnotation.context) && value instanceof String) { + context = ContextDescriptor.parse((String) value, parsingContext); + return; + } + if (name.equals(ExtractedAnnotation.checkRemoved) && value instanceof Boolean) { + isCheckRemoved = true; + return; + } + if (name.equals(ExtractedAnnotation.checkOptimizedOut) && value instanceof Boolean) { + isCheckOptimizedOut = true; return; } super.visit(name, value); } @Override - public AnnotationVisitor visitArray(String name) { - if (name.equals(Extracted.edges)) { - PropertyParsingContext parsingContext = getParsingContext().property(name); - ensureVersion(parsingContext); - return new AnnotationVisitorBase(parsingContext) { - @Override - public AnnotationVisitor visitAnnotation(String nullName, String descriptor) { - assert nullName == null; - if (descriptor.equals(Edge.DESCRIPTOR)) { - KeepEdgeVisitor visitor = - new KeepEdgeVisitor( - parsingContext.annotation(descriptor), edge -> {}, builder -> {}); - edgeVisitors.add(visitor); - return visitor; - } - return super.visitAnnotation(nullName, descriptor); - } - }; + public AnnotationVisitor visitAnnotation(String name, String descriptor) { + if (name.equals(ExtractedAnnotation.edge) && descriptor.equals(Edge.DESCRIPTOR)) { + edgeVisitor = + new KeepEdgeVisitor( + getParsingContext().annotation(descriptor), edge -> {}, builder -> {}); + return edgeVisitor; } - return super.visitArray(name); + return super.visitAnnotation(name, descriptor); } @Override public void visitEnd() { if (version == null) { - throw new KeepEdgeException("Invalid extracted edge, expected a version property."); + throw getParsingContext() + .error("Invalid extracted annotation, expected a version property."); } if (context == null) { - throw new KeepEdgeException("Invalid extracted edge, expected a context property."); + throw getParsingContext() + .error("Invalid extracted annotation, expected a context property."); } - for (KeepEdgeVisitor visitor : edgeVisitors) { + if (edgeVisitor != null) { + if (isCheckRemoved || isCheckOptimizedOut) { + throw getParsingContext() + .error("Invalid extracted annotation, cannot be both an edge and check."); + } parent.accept( - visitor + edgeVisitor .builder - .setMetaInfo( - visitor - .metaInfoBuilder - // TODO(b/323815449): This may be a method or field descriptor! - .setContextFromClassDescriptor(context) - .build()) + .setMetaInfo(context.applyToMetadata(edgeVisitor.metaInfoBuilder).build()) .build()); + return; } + if (isCheckRemoved && isCheckOptimizedOut) { + throw getParsingContext() + .error( + "Invalid extracted annotation, cannot be both a removed and optimized-out check."); + } + if (!isCheckRemoved && !isCheckOptimizedOut) { + throw getParsingContext() + .error( + "Invalid extracted annotation, must specify either an edge, a removed check, or an" + + " optimized-out check."); + } + KeepCheckKind kind = isCheckRemoved ? KeepCheckKind.REMOVED : KeepCheckKind.OPTIMIZED_OUT; + parent.accept( + KeepCheck.builder() + .setMetaInfo(context.applyToMetadata(KeepEdgeMetaInfo.builder()).build()) + .setKind(kind) + .setItemPattern(context.toItemPattern()) + .build()); super.visitEnd(); } } @@ -1371,6 +1438,7 @@ Item.classAnnotatedByClassNamePattern, ClassNameProperty.PATTERN); instanceOfParser = new InstanceOfParser(parsingContext); + instanceOfParser.setProperty(Item.instanceOfPattern, InstanceOfProperties.PATTERN); instanceOfParser.setProperty(Item.instanceOfClassName, InstanceOfProperties.NAME); instanceOfParser.setProperty(Item.instanceOfClassConstant, InstanceOfProperties.CONSTANT); instanceOfParser.setProperty( @@ -2173,4 +2241,5 @@ } } } + }
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 6774605..a0a98c7 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
@@ -4,34 +4,63 @@ package com.android.tools.r8.keepanno.asm; import com.android.tools.r8.keepanno.ast.AccessVisibility; +import com.android.tools.r8.keepanno.ast.AnnotationConstants; +import com.android.tools.r8.keepanno.ast.AnnotationConstants.AnnotationPattern; +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; -import com.android.tools.r8.keepanno.ast.AnnotationConstants.Extracted; +import com.android.tools.r8.keepanno.ast.AnnotationConstants.ExtractedAnnotation; +import com.android.tools.r8.keepanno.ast.AnnotationConstants.ExtractedAnnotations; +import com.android.tools.r8.keepanno.ast.AnnotationConstants.FieldAccess; +import com.android.tools.r8.keepanno.ast.AnnotationConstants.InstanceOfPattern; import com.android.tools.r8.keepanno.ast.AnnotationConstants.Item; +import com.android.tools.r8.keepanno.ast.AnnotationConstants.Kind; import com.android.tools.r8.keepanno.ast.AnnotationConstants.MemberAccess; +import com.android.tools.r8.keepanno.ast.AnnotationConstants.MethodAccess; +import com.android.tools.r8.keepanno.ast.AnnotationConstants.StringPattern; 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.KeepAnnotationPattern; +import com.android.tools.r8.keepanno.ast.KeepBindingReference; +import com.android.tools.r8.keepanno.ast.KeepBindings; import com.android.tools.r8.keepanno.ast.KeepClassItemPattern; +import com.android.tools.r8.keepanno.ast.KeepClassItemReference; import com.android.tools.r8.keepanno.ast.KeepConsequences; +import com.android.tools.r8.keepanno.ast.KeepConstraint; +import com.android.tools.r8.keepanno.ast.KeepConstraints; +import com.android.tools.r8.keepanno.ast.KeepDeclaration; import com.android.tools.r8.keepanno.ast.KeepEdge; +import com.android.tools.r8.keepanno.ast.KeepEdgeException; import com.android.tools.r8.keepanno.ast.KeepEdgeMetaInfo; +import com.android.tools.r8.keepanno.ast.KeepFieldAccessPattern; import com.android.tools.r8.keepanno.ast.KeepFieldPattern; +import com.android.tools.r8.keepanno.ast.KeepInstanceOfPattern; import com.android.tools.r8.keepanno.ast.KeepItemPattern; +import com.android.tools.r8.keepanno.ast.KeepItemReference; import com.android.tools.r8.keepanno.ast.KeepMemberAccessPattern; import com.android.tools.r8.keepanno.ast.KeepMemberItemPattern; import com.android.tools.r8.keepanno.ast.KeepMemberPattern; +import com.android.tools.r8.keepanno.ast.KeepMethodAccessPattern; import com.android.tools.r8.keepanno.ast.KeepMethodParametersPattern; import com.android.tools.r8.keepanno.ast.KeepMethodPattern; import com.android.tools.r8.keepanno.ast.KeepMethodReturnTypePattern; import com.android.tools.r8.keepanno.ast.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.ModifierPattern; import com.android.tools.r8.keepanno.ast.OptionalPattern; import com.android.tools.r8.keepanno.utils.Unimplemented; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Set; import java.util.function.BiFunction; import java.util.function.Consumer; import org.objectweb.asm.AnnotationVisitor; @@ -97,40 +126,106 @@ visitor.visitEnd(); } - public static void writeExtractedEdge( - KeepEdge edge, BiFunction<String, Boolean, AnnotationVisitorInterface> getVisitor) { + public static void writeExtractedEdges( + List<KeepDeclaration> declarations, + BiFunction<String, Boolean, AnnotationVisitorInterface> getVisitor) { + if (declarations.isEmpty()) { + return; + } withNewVisitor( - wrap(getVisitor.apply(Extracted.DESCRIPTOR, false)), - extractVisitor -> { - extractVisitor.visit("version", edge.getMetaInfo().getVersion().toVersionString()); - extractVisitor.visit("context", edge.getMetaInfo().getContextDescriptorString()); + wrap(getVisitor.apply(ExtractedAnnotations.DESCRIPTOR, false)), + containerVisitor -> + withNewVisitor( + containerVisitor.visitArray(ExtractedAnnotations.value), + arrayVisitor -> + declarations.forEach( + decl -> + withNewVisitor( + arrayVisitor.visitAnnotation(null, ExtractedAnnotation.DESCRIPTOR), + extractVisitor -> writeExtractedEdge(extractVisitor, decl))))); + } + + private static void writeExtractedEdge(AnnotationVisitor visitor, KeepDeclaration decl) { + KeepEdgeMetaInfo metaInfo = decl.getMetaInfo(); + visitor.visit(ExtractedAnnotation.version, metaInfo.getVersion().toVersionString()); + visitor.visit(ExtractedAnnotation.context, metaInfo.getContextDescriptorString()); + decl.match( + edge -> { withNewVisitor( - extractVisitor.visitArray("edges"), - edgeVisitor -> - writeEdgeInternal( - edge, (desc, visible) -> edgeVisitor.visitAnnotation(null, desc))); + visitor.visitAnnotation(ExtractedAnnotation.edge, Edge.DESCRIPTOR), + v -> new KeepEdgeWriter().writeEdge(edge, v)); + return null; + }, + check -> { + switch (check.getKind()) { + case REMOVED: + visitor.visit(ExtractedAnnotation.checkRemoved, true); + break; + case OPTIMIZED_OUT: + visitor.visit(ExtractedAnnotation.checkOptimizedOut, true); + break; + default: + throw new KeepEdgeException("Unexpected keep check kind: " + check.getKind()); + } + return null; }); } - public static void writeEdge( - KeepEdge edge, BiFunction<String, Boolean, AnnotationVisitorInterface> getVisitor) { - writeEdgeInternal(edge, (descriptor, visible) -> wrap(getVisitor.apply(descriptor, visible))); - } - - public static void writeEdgeInternal( - KeepEdge edge, BiFunction<String, Boolean, AnnotationVisitor> getVisitor) { - withNewVisitor( - getVisitor.apply(Edge.DESCRIPTOR, false), - visitor -> new KeepEdgeWriter().writeEdge(edge, visitor)); - } - private void writeEdge(KeepEdge edge, AnnotationVisitor visitor) { writeMetaInfo(visitor, edge.getMetaInfo()); + writeBindings(visitor, edge.getBindings()); writePreconditions(visitor, edge.getPreconditions()); writeConsequences(visitor, edge.getConsequences()); } - private void writeMetaInfo(AnnotationVisitor visitor, KeepEdgeMetaInfo metaInfo) {} + private void writeMetaInfo(AnnotationVisitor visitor, KeepEdgeMetaInfo metaInfo) { + // The edge version and context is written in the extraction header. + if (metaInfo.hasDescription()) { + visitor.visit(Edge.description, metaInfo.getDescriptionString()); + } + } + + private void writeBindings(AnnotationVisitor visitor, KeepBindings bindings) { + if (bindings.isEmpty()) { + return; + } + withNewVisitor( + visitor.visitArray(Edge.bindings), + arrayVisitor -> { + bindings.forEach( + (symbol, item) -> { + withNewVisitor( + arrayVisitor.visitAnnotation(null, Binding.DESCRIPTOR), + bindingVisitor -> { + bindingVisitor.visit(Binding.bindingName, symbol.toString()); + // The item is written directly into the binding annotation. + writeItem(bindingVisitor, item); + }); + }); + }); + } + + private void writeStringPattern( + KeepStringPattern stringPattern, String propertyName, AnnotationVisitor visitor) { + withNewVisitor( + visitor.visitAnnotation(propertyName, AnnotationConstants.StringPattern.DESCRIPTOR), + v -> { + if (stringPattern.isAny()) { + // The emtpy pattern matches any string. + return; + } + if (stringPattern.isExact()) { + v.visit(StringPattern.exact, stringPattern.asExactString()); + return; + } + if (stringPattern.hasPrefix()) { + v.visit(StringPattern.startsWith, stringPattern.getPrefixString()); + } + if (stringPattern.hasSuffix()) { + v.visit(StringPattern.endsWith, stringPattern.getSuffixString()); + } + }); + } private void writePreconditions(AnnotationVisitor visitor, KeepPreconditions preconditions) { if (preconditions.isAlways()) { @@ -144,12 +239,8 @@ condition -> withNewVisitor( arrayVisitor.visitAnnotation(ignoredArrayValueName, Condition.DESCRIPTOR), - conditionVisitor -> { - if (condition.getItem().isBindingReference()) { - throw new Unimplemented(); - } - writeItem(conditionVisitor, condition.getItem().asItemPattern()); - }))); + conditionVisitor -> + writeItemReference(conditionVisitor, condition.getItem())))); } private void writeConsequences(AnnotationVisitor visitor, KeepConsequences consequences) { @@ -162,12 +253,105 @@ target -> withNewVisitor( arrayVisitor.visitAnnotation(ignoredArrayValueName, Target.DESCRIPTOR), - targetVisitor -> { - if (target.getItem().isBindingReference()) { - throw new Unimplemented(); - } - writeItem(targetVisitor, target.getItem().asItemPattern()); - }))); + targetVisitor -> writeTarget(target, targetVisitor)))); + } + + private void writeTarget(KeepTarget target, AnnotationVisitor visitor) { + writeConstraints(visitor, target.getConstraints(), target.getItem()); + writeItemReference(visitor, target.getItem()); + } + + private void writeConstraints( + AnnotationVisitor visitor, KeepConstraints constraints, KeepItemReference item) { + Set<KeepConstraint> typedConstraints; + if (item.isClassItemReference()) { + typedConstraints = constraints.getClassConstraints(); + } else if (item.isMemberItemPattern()) { + KeepMemberPattern memberPattern = item.asMemberItemPattern().getMemberPattern(); + if (memberPattern.isMethod()) { + typedConstraints = constraints.getMethodConstraints(); + } else if (memberPattern.isField()) { + typedConstraints = constraints.getFieldConstraints(); + } else { + typedConstraints = constraints.getMemberConstraints(); + } + } else { + typedConstraints = constraints.getMemberConstraints(); + } + + List<String> constraintEnumValues = new ArrayList<>(); + List<KeepAnnotationPattern> annotationConstraints = new ArrayList<>(); + for (KeepConstraint constraint : typedConstraints) { + String value = constraint.getEnumValue(); + if (value != null) { + constraintEnumValues.add(value); + continue; + } + KeepAnnotationPattern annotationPattern = constraint.asAnnotationPattern(); + if (annotationPattern != null) { + annotationConstraints.add(annotationPattern); + continue; + } + throw new Unimplemented("Missing: " + constraint.getClass().toString()); + } + // The default constraints is *not* the empty set, so always write it as defined. + constraintEnumValues.sort(String::compareTo); + withNewVisitor( + visitor.visitArray(Target.constraints), + arrayVisitor -> + constraintEnumValues.forEach( + c -> arrayVisitor.visitEnum(null, Constraints.DESCRIPTOR, c))); + if (!annotationConstraints.isEmpty()) { + if (annotationConstraints.size() > 1) { + annotationConstraints.sort( + Comparator.comparing((KeepAnnotationPattern p) -> p.getNamePattern().toString()) + .thenComparingInt(p -> p.includesClassRetention() ? 1 : 0) + .thenComparingInt(p -> p.includesRuntimeRetention() ? 1 : 0)); + } + withNewVisitor( + visitor.visitArray(Target.constrainAnnotations), + arrayVisitor -> + annotationConstraints.forEach( + annotation -> { + withNewVisitor( + arrayVisitor.visitAnnotation(null, AnnotationPattern.DESCRIPTOR), + annoVisitor -> { + writeClassNamePattern( + annotation.getNamePattern(), + AnnotationPattern.namePattern, + annoVisitor); + assert annotation.includesClassRetention() + || annotation.includesRuntimeRetention(); + withNewVisitor( + annoVisitor.visitArray(AnnotationPattern.retention), + retentionArrayVisitor -> { + if (annotation.includesClassRetention()) { + retentionArrayVisitor.visitEnum( + null, + "Ljava/lang/annotation/RetentionPolicy;", + RetentionPolicy.CLASS.name()); + } + if (annotation.includesRuntimeRetention()) { + retentionArrayVisitor.visitEnum( + null, + "Ljava/lang/annotation/RetentionPolicy;", + RetentionPolicy.RUNTIME.name()); + } + }); + }); + })); + } + } + + private void writeItemReference(AnnotationVisitor visitor, KeepItemReference itemReference) { + if (itemReference.isBindingReference()) { + KeepBindingReference bindingReference = itemReference.asBindingReference(); + String bindingProperty = + bindingReference.isClassType() ? Item.classFromBinding : Item.memberFromBinding; + visitor.visit(bindingProperty, bindingReference.getName().toString()); + } else { + writeItem(visitor, itemReference.asItemPattern()); + } } private void writeItem(AnnotationVisitor itemVisitor, KeepItemPattern item) { @@ -186,25 +370,40 @@ itemVisitor); writeClassNamePattern( classItemPattern.getClassNamePattern(), Item.classNamePattern, itemVisitor); - if (!classItemPattern.getInstanceOfPattern().isAny()) { - throw new Unimplemented(); + writeInstanceOfPattern(classItemPattern.getInstanceOfPattern(), itemVisitor); + } + + private void writeInstanceOfPattern( + KeepInstanceOfPattern instanceOfPattern, AnnotationVisitor visitor) { + if (instanceOfPattern.isAny()) { + return; } + withNewVisitor( + visitor.visitAnnotation(Item.instanceOfPattern, InstanceOfPattern.DESCRIPTOR), + v -> { + v.visit(InstanceOfPattern.inclusive, instanceOfPattern.isInclusive()); + writeClassNamePattern( + instanceOfPattern.getClassNamePattern(), InstanceOfPattern.classNamePattern, v); + }); } private void writeMemberItem( KeepMemberItemPattern memberItemPattern, AnnotationVisitor itemVisitor) { - if (memberItemPattern.getClassReference().isBindingReference()) { - throw new Unimplemented(); + KeepClassItemReference classReference = memberItemPattern.getClassReference(); + if (classReference.isBindingReference()) { + KeepBindingReference bindingReference = classReference.asBindingReference(); + itemVisitor.visit(Item.classFromBinding, bindingReference.getName().toString()); + } else { + writeClassItem(classReference.asClassItemPattern(), itemVisitor); } - writeClassItem(memberItemPattern.getClassReference().asClassItemPattern(), itemVisitor); writeMember(memberItemPattern.getMemberPattern(), itemVisitor); } private void writeMember(KeepMemberPattern memberPattern, AnnotationVisitor targetVisitor) { if (memberPattern.isAllMembers()) { - throw new Unimplemented(); - } - if (memberPattern.isMethod()) { + // Due to the empty default being a class, we need to set the kind to members. + targetVisitor.visitEnum(Target.kind, Kind.DESCRIPTOR, Kind.ONLY_MEMBERS); + } else if (memberPattern.isMethod()) { writeMethod(memberPattern.asMethod(), targetVisitor); } else if (memberPattern.isField()) { writeField(memberPattern.asField(), targetVisitor); @@ -219,34 +418,26 @@ assert !member.isMethod(); writeAnnotatedBy( Item.memberAnnotatedByClassNamePattern, member.getAnnotatedByPattern(), targetVisitor); - writeAccessPattern(Item.memberAccess, member.getAccessPattern(), targetVisitor); + writeGeneralMemberAccessPattern(Item.memberAccess, member.getAccessPattern(), targetVisitor); } private void writeField(KeepFieldPattern field, AnnotationVisitor targetVisitor) { - String exactFieldName = field.getNamePattern().asExactString(); - if (exactFieldName != null) { - targetVisitor.visit(Item.fieldName, exactFieldName); - } else { - throw new Unimplemented(); - } - if (!field.getAccessPattern().isAny()) { - throw new Unimplemented(); - } + writeAnnotatedBy( + Item.fieldAnnotatedByClassNamePattern, field.getAnnotatedByPattern(), targetVisitor); + writeFieldAccessPattern(Item.fieldAccess, field.getAccessPattern(), targetVisitor); + writeStringPattern( + field.getNamePattern().asStringPattern(), Item.fieldNamePattern, targetVisitor); if (!field.getTypePattern().isAny()) { - throw new Unimplemented(); + writeTypePattern(field.getTypePattern().asType(), Item.fieldTypePattern, targetVisitor); } } private void writeMethod(KeepMethodPattern method, AnnotationVisitor targetVisitor) { - String exactMethodName = method.getNamePattern().asExactString(); - if (exactMethodName != null) { - targetVisitor.visit(Item.methodName, exactMethodName); - } else { - throw new Unimplemented(); - } - if (!method.getAccessPattern().isAny()) { - throw new Unimplemented(); - } + writeAnnotatedBy( + Item.methodAnnotatedByClassNamePattern, method.getAnnotatedByPattern(), targetVisitor); + writeMethodAccessPattern(Item.methodAccess, method.getAccessPattern(), targetVisitor); + writeStringPattern( + method.getNamePattern().asStringPattern(), Item.methodNamePattern, targetVisitor); writeMethodReturnType(method.getReturnTypePattern(), targetVisitor); writeMethodParameters(method.getParametersPattern(), targetVisitor); } @@ -343,13 +534,65 @@ } if (pattern.isOnlyPositive()) { arrayVisitor.visitEnum(null, desc, value); + return; } assert pattern.isOnlyNegative(); arrayVisitor.visitEnum(null, desc, MemberAccess.NEGATION_PREFIX + value); } - private void writeAccessPattern( + private void writeGeneralMemberAccessPattern( String propertyName, KeepMemberAccessPattern accessPattern, AnnotationVisitor targetVisitor) { + internalWriteAccessPattern( + propertyName, accessPattern, targetVisitor, MemberAccess.DESCRIPTOR, v -> {}); + } + + private void writeFieldAccessPattern( + String propertyName, KeepFieldAccessPattern accessPattern, AnnotationVisitor targetVisitor) { + String enumDescriptor = FieldAccess.DESCRIPTOR; + internalWriteAccessPattern( + propertyName, + accessPattern, + targetVisitor, + enumDescriptor, + visitor -> { + writeModifierEnumValue( + accessPattern.getVolatilePattern(), FieldAccess.VOLATILE, enumDescriptor, visitor); + writeModifierEnumValue( + accessPattern.getTransientPattern(), FieldAccess.TRANSIENT, enumDescriptor, visitor); + }); + } + + private void writeMethodAccessPattern( + String propertyName, KeepMethodAccessPattern accessPattern, AnnotationVisitor targetVisitor) { + String enumDescriptor = MethodAccess.DESCRIPTOR; + internalWriteAccessPattern( + propertyName, + accessPattern, + targetVisitor, + enumDescriptor, + visitor -> { + writeModifierEnumValue( + accessPattern.getSynchronizedPattern(), + MethodAccess.SYNCHRONIZED, + enumDescriptor, + visitor); + writeModifierEnumValue( + accessPattern.getBridgePattern(), MethodAccess.BRIDGE, enumDescriptor, visitor); + writeModifierEnumValue( + accessPattern.getNativePattern(), MethodAccess.NATIVE, enumDescriptor, visitor); + writeModifierEnumValue( + accessPattern.getAbstractPattern(), MethodAccess.ABSTRACT, enumDescriptor, visitor); + writeModifierEnumValue( + accessPattern.getStrictFpPattern(), MethodAccess.STRICT_FP, enumDescriptor, visitor); + }); + } + + private void internalWriteAccessPattern( + String propertyName, + KeepMemberAccessPattern accessPattern, + AnnotationVisitor targetVisitor, + String enumDescriptor, + Consumer<AnnotationVisitor> typeSpecificSettings) { if (accessPattern.isAny()) { return; } @@ -360,35 +603,28 @@ for (AccessVisibility visibility : accessPattern.getAllowedAccessVisibilities()) { switch (visibility) { case PUBLIC: - visitor.visitEnum(null, MemberAccess.DESCRIPTOR, MemberAccess.PUBLIC); + visitor.visitEnum(null, enumDescriptor, MemberAccess.PUBLIC); break; case PROTECTED: - visitor.visitEnum(null, MemberAccess.DESCRIPTOR, MemberAccess.PROTECTED); + visitor.visitEnum(null, enumDescriptor, MemberAccess.PROTECTED); break; case PACKAGE_PRIVATE: - visitor.visitEnum(null, MemberAccess.DESCRIPTOR, MemberAccess.PACKAGE_PRIVATE); + visitor.visitEnum(null, enumDescriptor, MemberAccess.PACKAGE_PRIVATE); break; case PRIVATE: - visitor.visitEnum(null, MemberAccess.DESCRIPTOR, MemberAccess.PRIVATE); + visitor.visitEnum(null, enumDescriptor, MemberAccess.PRIVATE); break; } } } writeModifierEnumValue( - accessPattern.getStaticPattern(), - MemberAccess.STATIC, - MemberAccess.DESCRIPTOR, - visitor); + accessPattern.getStaticPattern(), MemberAccess.STATIC, enumDescriptor, visitor); writeModifierEnumValue( - accessPattern.getFinalPattern(), - MemberAccess.FINAL, - MemberAccess.DESCRIPTOR, - visitor); + accessPattern.getFinalPattern(), MemberAccess.FINAL, enumDescriptor, visitor); writeModifierEnumValue( - accessPattern.getSyntheticPattern(), - MemberAccess.SYNTHETIC, - MemberAccess.DESCRIPTOR, - visitor); + accessPattern.getSyntheticPattern(), MemberAccess.SYNTHETIC, enumDescriptor, visitor); + + typeSpecificSettings.accept(visitor); }); } }
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 0789197..81ccbc6 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
@@ -15,12 +15,21 @@ * annotations which overlap in name with the actual semantic AST types. */ public final class AnnotationConstants { - public static final class Extracted { + public static final class ExtractedAnnotations { public static final String DESCRIPTOR = "Lcom/android/tools/r8/keepanno/annotations/ExtractedKeepAnnotations;"; + public static final String value = "value"; + } + + public static final class ExtractedAnnotation { + public static final String DESCRIPTOR = + "Lcom/android/tools/r8/keepanno/annotations/ExtractedKeepAnnotation;"; public static final String version = "version"; public static final String context = "context"; - public static final String edges = "edges"; + public static final String keepAnnotationGroup = "keep-annotation"; + public static final String edge = "edge"; + public static final String checkRemoved = "checkRemoved"; + public static final String checkOptimizedOut = "checkOptimizedOut"; } public static final class Edge { @@ -87,6 +96,7 @@ public static final String instanceOfClassConstant = "instanceOfClassConstant"; public static final String instanceOfClassConstantExclusive = "instanceOfClassConstantExclusive"; + public static final String instanceOfPattern = "instanceOfPattern"; public static final String classAnnotatedByGroup = "class-annotated-by"; public static final String classAnnotatedByClassName = "classAnnotatedByClassName"; public static final String classAnnotatedByClassConstant = "classAnnotatedByClassConstant"; @@ -231,10 +241,20 @@ public static final class ClassNamePattern { public static final String DESCRIPTOR = "Lcom/android/tools/r8/keepanno/annotations/ClassNamePattern;"; + public static final String classNameGroup = "class-name"; + public static final String name = "name"; + public static final String constant = "constant"; public static final String simpleName = "simpleName"; public static final String packageName = "packageName"; } + public static final class InstanceOfPattern { + public static final String DESCRIPTOR = + "Lcom/android/tools/r8/keepanno/annotations/InstanceOfPattern;"; + public static final String inclusive = "inclusive"; + public static final String classNamePattern = "classNamePattern"; + } + public static final class AnnotationPattern { public static final String DESCRIPTOR = "Lcom/android/tools/r8/keepanno/annotations/AnnotationPattern;";
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepConstraint.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepConstraint.java index 454f725..24ec53d 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepConstraint.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepConstraint.java
@@ -3,6 +3,7 @@ // 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.AnnotationConstants.Constraints; import com.android.tools.r8.keepanno.ast.KeepOptions.KeepOption; import java.util.Set; @@ -24,6 +25,12 @@ return System.identityHashCode(this); } + public abstract String getEnumValue(); + + public KeepAnnotationPattern asAnnotationPattern() { + return null; + } + public abstract void convertToDisallowKeepOptions(KeepOptions.Builder builder); public void addRequiredKeepAttributes(Set<KeepAttribute> attributes) { @@ -65,6 +72,11 @@ private Lookup() {} @Override + public String getEnumValue() { + return Constraints.LOOKUP; + } + + @Override public void convertToDisallowKeepOptions(KeepOptions.Builder builder) { builder.add(KeepOption.SHRINKING); } @@ -81,6 +93,11 @@ private Name() {} @Override + public String getEnumValue() { + return Constraints.NAME; + } + + @Override public void convertToDisallowKeepOptions(KeepOptions.Builder builder) { builder.add(KeepOption.OBFUSCATING); } @@ -97,6 +114,11 @@ private VisibilityRelax() {} @Override + public String getEnumValue() { + return Constraints.VISIBILITY_RELAX; + } + + @Override public void convertToDisallowKeepOptions(KeepOptions.Builder builder) { // The compiler currently satisfies that access is never restricted. } @@ -113,6 +135,11 @@ private VisibilityRestrict() {} @Override + public String getEnumValue() { + return Constraints.VISIBILITY_RESTRICT; + } + + @Override public void convertToDisallowKeepOptions(KeepOptions.Builder builder) { // We don't have directional rules so this prohibits any modification. builder.add(KeepOption.ACCESS_MODIFICATION); @@ -130,6 +157,11 @@ private NeverInline() {} @Override + public String getEnumValue() { + return Constraints.NEVER_INLINE; + } + + @Override public void convertToDisallowKeepOptions(KeepOptions.Builder builder) { builder.add(KeepOption.OPTIMIZING); } @@ -151,6 +183,11 @@ } @Override + public String getEnumValue() { + return Constraints.CLASS_INSTANTIATE; + } + + @Override public void convertToDisallowKeepOptions(KeepOptions.Builder builder) { builder.add(KeepOption.OPTIMIZING); } @@ -172,6 +209,11 @@ } @Override + public String getEnumValue() { + return Constraints.CLASS_OPEN_HIERARCHY; + } + + @Override public void convertToDisallowKeepOptions(KeepOptions.Builder builder) { builder.add(KeepOption.OPTIMIZING); } @@ -193,6 +235,11 @@ } @Override + public String getEnumValue() { + return Constraints.METHOD_INVOKE; + } + + @Override public void convertToDisallowKeepOptions(KeepOptions.Builder builder) { builder.add(KeepOption.OPTIMIZING); } @@ -214,6 +261,11 @@ } @Override + public String getEnumValue() { + return Constraints.METHOD_REPLACE; + } + + @Override public void convertToDisallowKeepOptions(KeepOptions.Builder builder) { builder.add(KeepOption.OPTIMIZING); } @@ -235,6 +287,11 @@ } @Override + public String getEnumValue() { + return Constraints.FIELD_GET; + } + + @Override public void convertToDisallowKeepOptions(KeepOptions.Builder builder) { builder.add(KeepOption.OPTIMIZING); } @@ -256,6 +313,11 @@ } @Override + public String getEnumValue() { + return Constraints.FIELD_SET; + } + + @Override public void convertToDisallowKeepOptions(KeepOptions.Builder builder) { builder.add(KeepOption.OPTIMIZING); } @@ -277,6 +339,11 @@ } @Override + public String getEnumValue() { + return Constraints.FIELD_REPLACE; + } + + @Override public void convertToDisallowKeepOptions(KeepOptions.Builder builder) { builder.add(KeepOption.OPTIMIZING); } @@ -325,6 +392,17 @@ } @Override + public KeepAnnotationPattern asAnnotationPattern() { + return annotationPattern; + } + + @Override + public String getEnumValue() { + // The annotation constraints cannot be represented by an enum value. + return null; + } + + @Override public void convertToDisallowKeepOptions(KeepOptions.Builder builder) { // The annotation constraint only implies that annotations should remain, no restrictions // are on the item otherwise. Also, we can't restrict the rule to just the annotations being
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepConstraints.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepConstraints.java index eb5cc2e..746d22a 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepConstraints.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepConstraints.java
@@ -11,6 +11,7 @@ import java.util.HashSet; import java.util.Objects; import java.util.Set; +import java.util.function.Predicate; import java.util.stream.Collectors; public abstract class KeepConstraints { @@ -70,11 +71,50 @@ } } + abstract Set<KeepConstraint> getConstraints(); + + private Set<KeepConstraint> getConstraintsMatching(Predicate<KeepConstraint> predicate) { + ImmutableSet.Builder<KeepConstraint> builder = ImmutableSet.builder(); + for (KeepConstraint constraint : getConstraints()) { + if (predicate.test(constraint)) { + builder.add(constraint); + } + } + return builder.build(); + } + + public final Set<KeepConstraint> getClassConstraints() { + return getConstraintsMatching(KeepConstraint::validForClass); + } + + public final Set<KeepConstraint> getMethodConstraints() { + return getConstraintsMatching(KeepConstraint::validForMethod); + } + + public final Set<KeepConstraint> getFieldConstraints() { + return getConstraintsMatching(KeepConstraint::validForField); + } + + public final Set<KeepConstraint> getMemberConstraints() { + return getConstraintsMatching(c -> c.validForMethod() || c.validForField()); + } + private static class Defaults extends KeepConstraints { private static final Defaults INSTANCE = new Defaults(); @Override + Set<KeepConstraint> getConstraints() { + return ImmutableSet.of( + KeepConstraint.lookup(), + KeepConstraint.name(), + KeepConstraint.classInstantiate(), + KeepConstraint.methodInvoke(), + KeepConstraint.fieldGet(), + KeepConstraint.fieldSet()); + } + + @Override public KeepOptions convertToKeepOptions(KeepOptions defaultOptions) { return defaultOptions; } @@ -100,6 +140,14 @@ } @Override + Set<KeepConstraint> getConstraints() { + return ImmutableSet.<KeepConstraint>builder() + .addAll(Defaults.INSTANCE.getConstraints()) + .addAll(additions.getConstraints()) + .build(); + } + + @Override public KeepOptions convertToKeepOptions(KeepOptions defaultOptions) { KeepOptions additionalOptions = additions.convertToKeepOptions(defaultOptions); KeepOptions.Builder builder = KeepOptions.disallowBuilder(); @@ -126,6 +174,11 @@ } @Override + public Set<KeepConstraint> getConstraints() { + return constraints; + } + + @Override public KeepOptions convertToKeepOptions(KeepOptions defaultOptions) { KeepOptions.Builder builder = KeepOptions.disallowBuilder(); for (KeepConstraint constraint : constraints) {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepDeclaration.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepDeclaration.java index c6f6248..4425317 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepDeclaration.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepDeclaration.java
@@ -3,11 +3,20 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.keepanno.ast; +import java.util.function.Function; + /** Base class for the declarations represented in the keep annoations library. */ public abstract class KeepDeclaration { public abstract KeepEdgeMetaInfo getMetaInfo(); + public final <T> T match(Function<KeepEdge, T> onEdge, Function<KeepCheck, T> onCheck) { + if (isKeepEdge()) { + return onEdge.apply(asKeepEdge()); + } + return onCheck.apply(asKeepCheck()); + } + public final boolean isKeepEdge() { return asKeepEdge() != null; }
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java index 7ade086..91d36c7 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassMetadataReader.java
@@ -25,7 +25,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; import kotlin.Metadata; -import kotlinx.metadata.jvm.JvmMetadataVersion; +import kotlinx.metadata.InconsistentKotlinMetadataException; import kotlinx.metadata.jvm.KotlinClassMetadata; import kotlinx.metadata.jvm.KotlinClassMetadata.FileFacade; import kotlinx.metadata.jvm.KotlinClassMetadata.MultiFileClassFacade; @@ -181,9 +181,9 @@ Integer xi = extraInt == null ? null : (Integer) extraInt.value.getBoxedValue(); try { - return KotlinClassMetadata.readStrict( + return KotlinClassMetadata.read( new KotlinMetadataAnnotationWrapper(k, mv, d1, d2, xs, pn, xi)); - } catch (ClassCastException | IllegalArgumentException | MetadataError e) { + } catch (ClassCastException | InconsistentKotlinMetadataException | MetadataError e) { throw new KotlinMetadataException(e); } } @@ -196,29 +196,15 @@ return (Integer) kind.value.getBoxedValue(); } - public static Metadata extractMetadataWithPossiblyUnsupportedMetadataVersion( - KotlinClassMetadata kMetadata) { - JvmMetadataVersion version = kMetadata.getVersion(); - if (version.getMajor() == 1 && version.getMinor() < 4) { - // From version 0.8.0 the kotlin metadata library cannot write metadata below Kotlin version - // 1.4. The writer can be tricked by temporarily setting a high enough version. - kMetadata.setVersion(KotlinJvmMetadataVersionUtils.MIN_SUPPORTED_VERSION); - Metadata metadata = kMetadata.write(); - kMetadata.setVersion(version); - return metadata; - } - return kMetadata.write(); - } - public static KotlinClassLevelInfo createKotlinInfo( Kotlin kotlin, DexClass clazz, KotlinClassMetadata kMetadata, AppView<?> appView, Consumer<DexEncodedMethod> keepByteCode) { - Metadata metadata = extractMetadataWithPossiblyUnsupportedMetadataVersion(kMetadata); + Metadata metadata = kMetadata.getAnnotationData$kotlinx_metadata_jvm(); String packageName = metadata.pn(); - int[] metadataVersion = KotlinJvmMetadataVersionUtils.toIntArray(kMetadata.getVersion()); + int[] metadataVersion = metadata.mv(); if (kMetadata instanceof KotlinClassMetadata.Class) { return KotlinClassInfo.create( (KotlinClassMetadata.Class) kMetadata,
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMetadataVersionUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMetadataVersionUtils.java deleted file mode 100644 index acf75d3..0000000 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMetadataVersionUtils.java +++ /dev/null
@@ -1,16 +0,0 @@ -// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -package com.android.tools.r8.kotlin; - -import kotlinx.metadata.jvm.JvmMetadataVersion; - -public class KotlinJvmMetadataVersionUtils { - - public static JvmMetadataVersion MIN_SUPPORTED_VERSION = new JvmMetadataVersion(1, 4, 0); - - public static int[] toIntArray(JvmMetadataVersion version) { - return new int[] {version.getMajor(), version.getMinor(), version.getPatch()}; - } -}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataAnnotationWrapper.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataAnnotationWrapper.java index 149fec9..d34a84f 100644 --- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataAnnotationWrapper.java +++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataAnnotationWrapper.java
@@ -56,12 +56,10 @@ } public static KotlinMetadataAnnotationWrapper wrap(KotlinClassMetadata classMetadata) { - Metadata annotationData = - KotlinClassMetadataReader.extractMetadataWithPossiblyUnsupportedMetadataVersion( - classMetadata); + Metadata annotationData = classMetadata.getAnnotationData$kotlinx_metadata_jvm(); return new KotlinMetadataAnnotationWrapper( annotationData.k(), - KotlinJvmMetadataVersionUtils.toIntArray(classMetadata.getVersion()), + annotationData.mv(), annotationData.d1(), annotationData.d2(), annotationData.xs(),
diff --git a/src/main/java/com/android/tools/r8/naming/KotlinModuleSynthesizer.java b/src/main/java/com/android/tools/r8/naming/KotlinModuleSynthesizer.java index 76c2314..9504a38 100644 --- a/src/main/java/com/android/tools/r8/naming/KotlinModuleSynthesizer.java +++ b/src/main/java/com/android/tools/r8/naming/KotlinModuleSynthesizer.java
@@ -24,9 +24,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import kotlinx.metadata.jvm.JvmMetadataVersion; -import kotlinx.metadata.jvm.KmModule; -import kotlinx.metadata.jvm.KotlinModuleMetadata; +import kotlinx.metadata.jvm.KotlinModuleMetadata.Writer; /** * The kotlin module synthesizer will scan through all file facades and multiclass files to figure @@ -157,7 +155,7 @@ } } Collections.sort(packagesSorted); - KmModule kmModule = new KmModule(); + Writer writer = new Writer(); for (String newPackage : packagesSorted) { // Calling other visitors than visitPackageParts are currently not supported. // https://github.com/JetBrains/kotlin/blob/master/libraries/kotlinx-metadata/ @@ -176,15 +174,14 @@ newMultiFiles.put(classPart, rewrittenName); }); }); - kmModule.visitPackageParts( + writer.visitPackageParts( newPackage, newFacades.getOrDefault(newPackage, Collections.emptyList()), newMultiFiles); } return Optional.of( DataEntryResource.fromBytes( - new KotlinModuleMetadata(kmModule, new JvmMetadataVersion(metadataVersion.get())) - .write(), + writer.write(metadataVersion.get()), "META-INF/" + moduleName + ".kotlin_module", Origin.unknown())); }
diff --git a/src/test/java/com/android/tools/r8/keepanno/CheckOptimizedOutAnnotationTest.java b/src/test/java/com/android/tools/r8/keepanno/CheckOptimizedOutAnnotationTest.java index cfe7d75..59915bc9 100644 --- a/src/test/java/com/android/tools/r8/keepanno/CheckOptimizedOutAnnotationTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/CheckOptimizedOutAnnotationTest.java
@@ -57,7 +57,6 @@ public void testR8Native() throws Throwable { assumeTrue(parameters.isR8() && parameters.isNative()); testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getInputClasses()) .addKeepMainRule(TestClass.class) .applyIfR8Native(
diff --git a/src/test/java/com/android/tools/r8/keepanno/CheckRemovedAnnotationTest.java b/src/test/java/com/android/tools/r8/keepanno/CheckRemovedAnnotationTest.java index a1a4a2c..b3bedf1 100644 --- a/src/test/java/com/android/tools/r8/keepanno/CheckRemovedAnnotationTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/CheckRemovedAnnotationTest.java
@@ -56,7 +56,6 @@ public void testR8Native() throws Exception { assumeTrue(parameters.isR8() && parameters.isNative()); testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getInputClasses()) .addKeepMainRule(TestClass.class) .applyIfR8Native(
diff --git a/src/test/java/com/android/tools/r8/keepanno/FieldNameStringPatternsTest.java b/src/test/java/com/android/tools/r8/keepanno/FieldNameStringPatternsTest.java index d804b85..d6a6b90 100644 --- a/src/test/java/com/android/tools/r8/keepanno/FieldNameStringPatternsTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/FieldNameStringPatternsTest.java
@@ -40,7 +40,6 @@ @Test public void test() throws Exception { testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getInputClasses()) .setExcludedOuterClass(getClass()) .run(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/keepanno/FieldPatternsTest.java b/src/test/java/com/android/tools/r8/keepanno/FieldPatternsTest.java index 4c62801..669d945 100644 --- a/src/test/java/com/android/tools/r8/keepanno/FieldPatternsTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/FieldPatternsTest.java
@@ -38,7 +38,6 @@ @Test public void test() throws Exception { testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getInputClasses()) .addKeepMainRule(TestClass.class) .setExcludedOuterClass(getClass())
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepAccessVisibilityFlagsTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepAccessVisibilityFlagsTest.java index 4eec939..a9dec7d 100644 --- a/src/test/java/com/android/tools/r8/keepanno/KeepAccessVisibilityFlagsTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/KeepAccessVisibilityFlagsTest.java
@@ -82,7 +82,6 @@ @Test public void test() throws Exception { testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getInputClasses()) .addKeepMainRule(TestClass.class) .allowAccessModification()
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepAnnoParameters.java b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoParameters.java index de359e4..53c7634 100644 --- a/src/test/java/com/android/tools/r8/keepanno/KeepAnnoParameters.java +++ b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoParameters.java
@@ -12,7 +12,8 @@ public enum KeepAnnoConfig { REFERENCE, - R8_NATIVE, + R8_DIRECT, + R8_EXTRACT, R8_LEGACY, PG; } @@ -55,7 +56,9 @@ } public boolean isR8() { - return config == KeepAnnoConfig.R8_NATIVE || config == KeepAnnoConfig.R8_LEGACY; + return config == KeepAnnoConfig.R8_DIRECT + || config == KeepAnnoConfig.R8_EXTRACT + || config == KeepAnnoConfig.R8_LEGACY; } public boolean isPG() { @@ -63,10 +66,12 @@ } public boolean isNative() { - return config == KeepAnnoConfig.R8_NATIVE; + return config == KeepAnnoConfig.R8_DIRECT || config == KeepAnnoConfig.R8_EXTRACT; } public boolean isExtract() { - return config == KeepAnnoConfig.R8_LEGACY || config == KeepAnnoConfig.PG; + return config == KeepAnnoConfig.R8_EXTRACT + || config == KeepAnnoConfig.R8_LEGACY + || config == KeepAnnoConfig.PG; } }
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBase.java b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBase.java index 698e0bc..86d9529 100644 --- a/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBase.java +++ b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBase.java
@@ -20,7 +20,9 @@ keepAnnoParams.add( new KeepAnnoParameters(parameters, KeepAnnoParameters.KeepAnnoConfig.REFERENCE)); keepAnnoParams.add( - new KeepAnnoParameters(parameters, KeepAnnoParameters.KeepAnnoConfig.R8_NATIVE)); + new KeepAnnoParameters(parameters, KeepAnnoParameters.KeepAnnoConfig.R8_DIRECT)); + keepAnnoParams.add( + new KeepAnnoParameters(parameters, KeepAnnoParameters.KeepAnnoConfig.R8_EXTRACT)); keepAnnoParams.add( new KeepAnnoParameters(parameters, KeepAnnoParameters.KeepAnnoConfig.R8_LEGACY)); if (parameters.isCfRuntime()) {
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBuilder.java b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBuilder.java index 1ec18af..e9d6b40 100644 --- a/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBuilder.java +++ b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBuilder.java
@@ -20,9 +20,8 @@ import com.android.tools.r8.keepanno.asm.KeepEdgeReader; import com.android.tools.r8.keepanno.asm.KeepEdgeWriter; import com.android.tools.r8.keepanno.ast.KeepDeclaration; -import com.android.tools.r8.keepanno.ast.KeepEdge; import com.android.tools.r8.keepanno.keeprules.KeepRuleExtractorOptions; -import com.android.tools.r8.keepanno.utils.Unimplemented; +import com.android.tools.r8.origin.Origin; import com.android.tools.r8.utils.DescriptorUtils; import com.android.tools.r8.utils.InternalOptions; import java.io.IOException; @@ -41,8 +40,10 @@ switch (params.config()) { case REFERENCE: return new ReferenceBuilder(params, temp); - case R8_NATIVE: - return new R8NativeBuilder(params, temp); + case R8_DIRECT: + return new R8NativeBuilder(false, params, temp); + case R8_EXTRACT: + return new R8NativeBuilder(true, params, temp); case R8_LEGACY: return new R8LegacyBuilder(params, temp); case PG: @@ -120,10 +121,6 @@ return inspectOutputConfig(System.out::println); } - public KeepAnnoTestBuilder skipEdgeExtraction() { - return this; - } - public KeepAnnoTestBuilder inspectOutputConfig(Consumer<String> configConsumer) { // Default to ignore the consumer. return this; @@ -166,24 +163,23 @@ private final R8FullTestBuilder builder; private List<Consumer<R8TestCompileResult>> compileResultConsumers = new ArrayList<>(); - private boolean useEdgeExtraction = true; + private final boolean useEdgeExtraction; - public R8NativeBuilder(KeepAnnoParameters params, TemporaryFolder temp) { + private R8NativeBuilder( + boolean useEdgeExtraction, KeepAnnoParameters params, TemporaryFolder temp) { super(params, temp); builder = TestBase.testForR8(temp, parameters().getBackend()) .enableExperimentalKeepAnnotations() .setMinApi(parameters()); - builder.getBuilder().setEnableExperimentalKeepAnnotations(false); - builder.getBuilder().setEnableExperimentalVersionedKeepEdgeAnnotations(true); - } - - @Override - public KeepAnnoTestBuilder skipEdgeExtraction() { - useEdgeExtraction = false; - builder.getBuilder().setEnableExperimentalKeepAnnotations(true); - builder.getBuilder().setEnableExperimentalVersionedKeepEdgeAnnotations(false); - return this; + this.useEdgeExtraction = useEdgeExtraction; + if (useEdgeExtraction) { + builder.getBuilder().setEnableExperimentalKeepAnnotations(false); + builder.getBuilder().setEnableExperimentalVersionedKeepEdgeAnnotations(true); + } else { + builder.getBuilder().setEnableExperimentalKeepAnnotations(true); + builder.getBuilder().setEnableExperimentalVersionedKeepEdgeAnnotations(false); + } } @Override @@ -224,7 +220,7 @@ return this; } - private void extractAndAdd(byte[] classFileData) throws IOException { + private void extractAndAdd(byte[] classFileData) { builder.addProgramClassFileData(classFileData); if (useEdgeExtraction) { List<KeepDeclaration> declarations = KeepEdgeReader.readKeepEdges(classFileData); @@ -241,19 +237,21 @@ null, "java/lang/Object", null); - for (KeepDeclaration decl : declarations) { - if (!decl.isKeepEdge()) { - throw new Unimplemented("Support check declarations..."); - } else { - KeepEdge edge = decl.asKeepEdge(); - KeepEdgeWriter.writeExtractedEdge( - edge, - (descriptor, visible) -> - KeepAnnoTestUtils.wrap(classWriter.visitAnnotation(descriptor, visible))); - } - } + KeepEdgeWriter.writeExtractedEdges( + declarations, + (descriptor, visible) -> + KeepAnnoTestUtils.wrap(classWriter.visitAnnotation(descriptor, visible))); classWriter.visitEnd(); - builder.addProgramClassFileData(classWriter.toByteArray()); + builder + .getBuilder() + .addClassProgramData( + classWriter.toByteArray(), + new Origin(Origin.root()) { + @Override + public String part() { + return "edge-extraction"; + } + }); } } }
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepAnnotationViaSuperTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepAnnotationViaSuperTest.java index 5e54002..a9dd273 100644 --- a/src/test/java/com/android/tools/r8/keepanno/KeepAnnotationViaSuperTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/KeepAnnotationViaSuperTest.java
@@ -8,6 +8,8 @@ import static org.hamcrest.MatcherAssert.assertThat; import com.android.tools.r8.keepanno.annotations.AnnotationPattern; +import com.android.tools.r8.keepanno.annotations.ClassNamePattern; +import com.android.tools.r8.keepanno.annotations.InstanceOfPattern; import com.android.tools.r8.keepanno.annotations.KeepTarget; import com.android.tools.r8.keepanno.annotations.UsesReflection; import com.android.tools.r8.utils.AndroidApiLevel; @@ -41,7 +43,6 @@ @Test public void test() throws Exception { testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getInputClasses()) .addKeepMainRule(TestClass.class) .setExcludedOuterClass(getClass()) @@ -92,7 +93,10 @@ @UsesReflection({ @KeepTarget( - instanceOfClassConstantExclusive = Base.class, + instanceOfPattern = + @InstanceOfPattern( + inclusive = false, + classNamePattern = @ClassNamePattern(constant = Base.class)), constraints = {}, constrainAnnotations = @AnnotationPattern(constant = Anno.class)) })
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepBindingTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepBindingTest.java index 02136bb..464ee91 100644 --- a/src/test/java/com/android/tools/r8/keepanno/KeepBindingTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/KeepBindingTest.java
@@ -37,7 +37,6 @@ @Test public void test() throws Exception { testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getInputClasses()) .addKeepClassRules(A.class, B.class) .addKeepMainRule(TestClass.class) @@ -50,7 +49,6 @@ @Test public void testNoKeepOnClass() throws Exception { testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getInputClasses()) .addKeepMainRule(TestClass.class) .setExcludedOuterClass(getClass())
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepEmptyClassTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepEmptyClassTest.java index 4abd731..c1e5683 100644 --- a/src/test/java/com/android/tools/r8/keepanno/KeepEmptyClassTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/KeepEmptyClassTest.java
@@ -37,7 +37,6 @@ @Test public void test() throws Exception { testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getInputClasses()) .addProgramClassFileData( transformer(B.class).removeMethods(MethodPredicate.all()).transform())
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepFieldValueApiTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepFieldValueApiTest.java index 20f8858..9ac84d1 100644 --- a/src/test/java/com/android/tools/r8/keepanno/KeepFieldValueApiTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/KeepFieldValueApiTest.java
@@ -58,7 +58,6 @@ assertTrue(parameters.isShrinker()); Box<Path> lib = new Box<>(); testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getLibraryClasses()) .setExcludedOuterClass(getClass()) .applyIfShrinker(b -> lib.set(b.compile().inspect(this::checkLibraryOutput).writeToZip()));
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepFooIfBarSameClassTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepFooIfBarSameClassTest.java index 121b904..0a2e16b 100644 --- a/src/test/java/com/android/tools/r8/keepanno/KeepFooIfBarSameClassTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/KeepFooIfBarSameClassTest.java
@@ -39,7 +39,6 @@ @Test public void test() throws Exception { testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getInputClasses()) .addKeepMainRule(TestClass.class) .setExcludedOuterClass(getClass())
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepInclusiveInstanceOfTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepInclusiveInstanceOfTest.java index 895f7e7..85809df 100644 --- a/src/test/java/com/android/tools/r8/keepanno/KeepInclusiveInstanceOfTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/KeepInclusiveInstanceOfTest.java
@@ -30,7 +30,6 @@ @Test public void test() throws Exception { testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getInputClasses()) .addKeepMainRule(TestClass.class) .setExcludedOuterClass(getClass())
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepMembersApiTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepMembersApiTest.java index fe9b59c..c6d3b7d 100644 --- a/src/test/java/com/android/tools/r8/keepanno/KeepMembersApiTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/KeepMembersApiTest.java
@@ -58,7 +58,6 @@ assertTrue(parameters.isShrinker()); Box<Path> lib = new Box<>(); testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getLibraryClasses()) .setExcludedOuterClass(getClass()) .applyIfShrinker(b -> lib.set(b.compile().inspect(this::checkLibraryOutput).writeToZip()));
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepMethodsAccessFlagsTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepMethodsAccessFlagsTest.java index d560087..0de2eaf 100644 --- a/src/test/java/com/android/tools/r8/keepanno/KeepMethodsAccessFlagsTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/KeepMethodsAccessFlagsTest.java
@@ -41,7 +41,6 @@ @Test public void test() throws Exception { testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getInputClasses()) .addKeepMainRule(TestClass.class) .setExcludedOuterClass(getClass())
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepMethodsEmptyAccessFlagsTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepMethodsEmptyAccessFlagsTest.java index 7ebe37e..cac4ddb 100644 --- a/src/test/java/com/android/tools/r8/keepanno/KeepMethodsEmptyAccessFlagsTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/KeepMethodsEmptyAccessFlagsTest.java
@@ -40,7 +40,6 @@ @Test public void test() throws Exception { testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getInputClasses()) .addKeepMainRule(TestClass.class) .setExcludedOuterClass(getClass())
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepNameAndInstanceOfTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepNameAndInstanceOfTest.java index b59ee95..b36cca3 100644 --- a/src/test/java/com/android/tools/r8/keepanno/KeepNameAndInstanceOfTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/KeepNameAndInstanceOfTest.java
@@ -31,7 +31,6 @@ @Test public void test() throws Exception { testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getInputClasses()) .addKeepMainRule(TestClass.class) .setExcludedOuterClass(getClass())
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepSameMethodTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepSameMethodTest.java index f7f57ce..f1fe6f5 100644 --- a/src/test/java/com/android/tools/r8/keepanno/KeepSameMethodTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/KeepSameMethodTest.java
@@ -38,11 +38,10 @@ @Test public void test() throws Exception { testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getInputClasses()) .addKeepMainRule(TestClass.class) .setExcludedOuterClass(getClass()) - // The "all members" target will create an unused "all fields" rule. + // The extraction based "all members" target will create an unused "all fields" rule. .allowUnusedProguardConfigurationRules() .run(TestClass.class) .assertSuccessWithOutput(EXPECTED)
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepStaticBindingTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepStaticBindingTest.java index 21260a5..1fbe385 100644 --- a/src/test/java/com/android/tools/r8/keepanno/KeepStaticBindingTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/KeepStaticBindingTest.java
@@ -36,7 +36,6 @@ @Test public void test() throws Exception { testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getInputClasses()) .setExcludedOuterClass(getClass()) .run(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepTargetClassAndMemberKindTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepTargetClassAndMemberKindTest.java index e3a9569..9afb8b1 100644 --- a/src/test/java/com/android/tools/r8/keepanno/KeepTargetClassAndMemberKindTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/KeepTargetClassAndMemberKindTest.java
@@ -36,7 +36,6 @@ @Test public void test() throws Exception { testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getInputClasses()) .addKeepMainRule(TestClass.class) .setExcludedOuterClass(getClass())
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepUsedByNativeAnnotationTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepUsedByNativeAnnotationTest.java index 79a1720..dfe0e2b 100644 --- a/src/test/java/com/android/tools/r8/keepanno/KeepUsedByNativeAnnotationTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/KeepUsedByNativeAnnotationTest.java
@@ -40,7 +40,6 @@ @Test public void test() throws Exception { testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getInputClasses()) .addKeepMainRule(TestClass.class) .setExcludedOuterClass(getClass())
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationTest.java index 988e441..1c0313a 100644 --- a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationTest.java
@@ -35,7 +35,6 @@ @Test public void test() throws Exception { testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getInputClasses()) .addKeepMainRule(TestClass.class) .setExcludedOuterClass(getClass())
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionFieldAnnotationTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionFieldAnnotationTest.java index 481cc35..92f2515 100644 --- a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionFieldAnnotationTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionFieldAnnotationTest.java
@@ -35,7 +35,6 @@ @Test public void test() throws Exception { testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getInputClasses()) .addKeepMainRule(TestClass.class) .setExcludedOuterClass(getClass())
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionOnFieldTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionOnFieldTest.java index 544fcad..e1c5dd9 100644 --- a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionOnFieldTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionOnFieldTest.java
@@ -38,7 +38,6 @@ @Test public void test() throws Exception { testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getInputClasses()) .addKeepMainRule(TestClass.class) .setExcludedOuterClass(getClass())
diff --git a/src/test/java/com/android/tools/r8/keepanno/MembersAnnotatedByPatternsTest.java b/src/test/java/com/android/tools/r8/keepanno/MembersAnnotatedByPatternsTest.java index 820b78b..52850da 100644 --- a/src/test/java/com/android/tools/r8/keepanno/MembersAnnotatedByPatternsTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/MembersAnnotatedByPatternsTest.java
@@ -47,7 +47,6 @@ @Test public void test() throws Exception { testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getInputClasses()) .setExcludedOuterClass(getClass()) .run(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/keepanno/MethodNameStringPatternsTest.java b/src/test/java/com/android/tools/r8/keepanno/MethodNameStringPatternsTest.java index bfb85ad..9306ca4 100644 --- a/src/test/java/com/android/tools/r8/keepanno/MethodNameStringPatternsTest.java +++ b/src/test/java/com/android/tools/r8/keepanno/MethodNameStringPatternsTest.java
@@ -40,7 +40,6 @@ @Test public void test() throws Exception { testForKeepAnno(parameters) - .skipEdgeExtraction() .addProgramClasses(getInputClasses()) .setExcludedOuterClass(getClass()) .run(TestClass.class)
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 977afd5..325960e 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
@@ -73,6 +73,7 @@ private static final ClassReference STRING_PATTERN = annoClass("StringPattern"); private static final ClassReference TYPE_PATTERN = annoClass("TypePattern"); private static final ClassReference CLASS_NAME_PATTERN = annoClass("ClassNamePattern"); + private static final ClassReference INSTANCE_OF_PATTERN = annoClass("InstanceOfPattern"); private static final ClassReference ANNOTATION_PATTERN = annoClass("AnnotationPattern"); private static final ClassReference USES_REFLECTION = annoClass("UsesReflection"); private static final ClassReference USED_BY_REFLECTION = annoClass("UsedByReflection"); @@ -81,6 +82,8 @@ private static final ClassReference CHECK_OPTIMIZED_OUT = annoClass("CheckOptimizedOut"); private static final ClassReference EXTRACTED_KEEP_ANNOTATIONS = annoClass("ExtractedKeepAnnotations"); + private static final ClassReference EXTRACTED_KEEP_ANNOTATION = + annoClass("ExtractedKeepAnnotation"); private static final ClassReference KEEP_EDGE = annoClass("KeepEdge"); private static final ClassReference KEEP_BINDING = annoClass("KeepBinding"); private static final ClassReference KEEP_TARGET = annoClass("KeepTarget"); @@ -200,6 +203,8 @@ "@" + simpleName(TYPE_PATTERN) + "(name = \"\")"; private static final String DEFAULT_INVALID_CLASS_NAME_PATTERN = "@" + simpleName(CLASS_NAME_PATTERN) + "(simpleName = \"\")"; + private static final String DEFAULT_ANY_INSTANCE_OF_PATTERN = + "@" + simpleName(INSTANCE_OF_PATTERN) + "()"; private static ClassReference astClass(String simpleName) { return classFromTypeName(AST_PKG + simpleName); @@ -277,6 +282,11 @@ return requiredValue(JAVA_STRING); } + public GroupMember defaultBooleanValue(boolean value) { + setType("boolean"); + return setValue(value ? "true" : "false"); + } + public GroupMember defaultValue(ClassReference type, String value) { setType(simpleName(type)); return setValue(value); @@ -591,6 +601,44 @@ // boolean anyReference() default false; } + private Group instanceOfPatternInclusive() { + return new Group("instance-of-inclusive") + .addMember( + new GroupMember("inclusive") + .setDocTitle("True if the pattern should include the directly matched classes.") + .addParagraph( + "If false, the pattern is exclusive and only matches classes that are", + "strict subclasses of the pattern.") + .defaultBooleanValue(true)); + } + + private Group instanceOfPatternClassNamePattern() { + return new Group("instance-of-class-name-pattern") + .addMember( + new GroupMember("classNamePattern") + .setDocTitle("Instances of classes matching the class-name pattern.") + .defaultValue(CLASS_NAME_PATTERN, DEFAULT_INVALID_CLASS_NAME_PATTERN)); + } + + private Group classNamePatternFullNameGroup() { + return new Group(CLASS_NAME_GROUP) + .allowMutuallyExclusiveWithOtherGroups() + .addMember( + new GroupMember("name") + .setDocTitle( + "Define the " + CLASS_NAME_GROUP + " pattern by fully qualified class name.") + .setDocReturn("The qualified class name that defines the class.") + .defaultEmptyString()) + .addMember( + new GroupMember("constant") + .setDocTitle( + "Define the " + + CLASS_NAME_GROUP + + " pattern by reference to a Class constant.") + .setDocReturn("The class-constant that defines the class.") + .defaultObjectClass()); + } + private Group classNamePatternSimpleNameGroup() { return new Group("class-simple-name") .addMember( @@ -848,12 +896,20 @@ .defaultObjectClass(); } + private GroupMember instanceOfPattern() { + return new GroupMember("instanceOfPattern") + .setDocTitle("Define the " + INSTANCE_OF_GROUP + " with a pattern.") + .setDocReturn("The pattern that defines what instance-of the class must be.") + .defaultValue(INSTANCE_OF_PATTERN, DEFAULT_ANY_INSTANCE_OF_PATTERN); + } + private Group createClassInstanceOfPatternGroup() { return new Group(INSTANCE_OF_GROUP) .addMember(instanceOfClassName()) .addMember(instanceOfClassNameExclusive()) .addMember(instanceOfClassConstant()) .addMember(instanceOfClassConstantExclusive()) + .addMember(instanceOfPattern()) .addDocFooterParagraph( "If none are specified the default is to match any class instance."); } @@ -1235,9 +1291,38 @@ println(); withIndent( () -> { - classNamePatternSimpleNameGroup().generate(this); + Group exactNameGroup = classNamePatternFullNameGroup(); + Group simpleNameGroup = classNamePatternSimpleNameGroup(); + Group packageGroup = classNamePatternPackageGroup(); + exactNameGroup.addMutuallyExclusiveGroups(simpleNameGroup, packageGroup); + + exactNameGroup.generate(this); println(); - classNamePatternPackageGroup().generate(this); + simpleNameGroup.generate(this); + println(); + packageGroup.generate(this); + }); + println(); + println("}"); + } + + private void generateInstanceOfPattern() { + printCopyRight(2024); + printPackage("annotations"); + printImports(ANNOTATION_IMPORTS); + DocPrinter.printer() + .setDocTitle("A pattern structure for matching instances of classes and interfaces.") + .addParagraph("If no properties are set, the default pattern matches any instance.") + .printDoc(this::println); + println("@Target(ElementType.ANNOTATION_TYPE)"); + println("@Retention(RetentionPolicy.CLASS)"); + println("public @interface " + simpleName(INSTANCE_OF_PATTERN) + " {"); + println(); + withIndent( + () -> { + instanceOfPatternInclusive().generate(this); + println(); + instanceOfPatternClassNamePattern().generate(this); }); println(); println("}"); @@ -1608,6 +1693,7 @@ generateStringPatternConstants(); generateTypePatternConstants(); generateClassNamePatternConstants(); + generateInstanceOfPatternConstants(); generateAnnotationPatternConstants(); }); println("}"); @@ -1619,10 +1705,21 @@ } private void generateExtractedKeepAnnotationsConstants() { - println("public static final class Extracted {"); + println("public static final class ExtractedAnnotations {"); withIndent( () -> { generateAnnotationConstants(EXTRACTED_KEEP_ANNOTATIONS); + new GroupMember("value") + .setDocTitle("Extracted normalized keep edges.") + .requiredArrayValue(KEEP_EDGE) + .generateConstants(this); + }); + println("}"); + println(); + println("public static final class ExtractedAnnotation {"); + withIndent( + () -> { + generateAnnotationConstants(EXTRACTED_KEEP_ANNOTATION); new GroupMember("version") .setDocTitle("Extraction version used to generate this keep annotation.") .requiredStringValue() @@ -1631,9 +1728,19 @@ .setDocTitle("Extraction context from which this keep annotation is generated.") .requiredStringValue() .generateConstants(this); - new GroupMember("edges") - .setDocTitle("Extracted normalized keep edges.") - .requiredArrayValue(KEEP_EDGE) + new Group("keep-annotation") + .addMember( + new GroupMember("edge") + .setDocTitle("Extracted normalized keep edge.") + .requiredValue(KEEP_EDGE)) + .addMember( + new GroupMember("checkRemoved") + .setDocTitle("Extracted check removed.") + .defaultBooleanValue(false)) + .addMember( + new GroupMember("checkOptimizedOut") + .setDocTitle("Extracted check optimized out.") + .defaultBooleanValue(false)) .generateConstants(this); }); println("}"); @@ -1919,6 +2026,7 @@ withIndent( () -> { generateAnnotationConstants(CLASS_NAME_PATTERN); + classNamePatternFullNameGroup().generateConstants(this); classNamePatternSimpleNameGroup().generateConstants(this); classNamePatternPackageGroup().generateConstants(this); }); @@ -1926,6 +2034,18 @@ println(); } + private void generateInstanceOfPatternConstants() { + println("public static final class InstanceOfPattern {"); + withIndent( + () -> { + generateAnnotationConstants(INSTANCE_OF_PATTERN); + instanceOfPatternInclusive().generateConstants(this); + instanceOfPatternClassNamePattern().generateConstants(this); + }); + println("}"); + println(); + } + private void generateAnnotationPatternConstants() { println("public static final class AnnotationPattern {"); withIndent( @@ -1967,6 +2087,7 @@ writeFile(source(STRING_PATTERN), Generator::generateStringPattern, write); writeFile(source(TYPE_PATTERN), Generator::generateTypePattern, write); writeFile(source(CLASS_NAME_PATTERN), Generator::generateClassNamePattern, write); + writeFile(source(INSTANCE_OF_PATTERN), Generator::generateInstanceOfPattern, write); writeFile(source(ANNOTATION_PATTERN), Generator::generateAnnotationPattern, write); writeFile(source(KEEP_BINDING), Generator::generateKeepBinding, write); writeFile(source(KEEP_TARGET), Generator::generateKeepTarget, write);
diff --git a/third_party/dependencies.tar.gz.sha1 b/third_party/dependencies.tar.gz.sha1 index e6e6581..a916904 100644 --- a/third_party/dependencies.tar.gz.sha1 +++ b/third_party/dependencies.tar.gz.sha1
@@ -1 +1 @@ -799dc154db4bd7687d0f22b90590555da831ef95 \ No newline at end of file +b359a23d49694aabe18bf8c47b2bf3e5b1b77322 \ No newline at end of file
diff --git a/third_party/dependencies_plugin.tar.gz.sha1 b/third_party/dependencies_plugin.tar.gz.sha1 index f85351e..d6ec64f 100644 --- a/third_party/dependencies_plugin.tar.gz.sha1 +++ b/third_party/dependencies_plugin.tar.gz.sha1
@@ -1 +1 @@ -a58a2edb72ff3a3126e4846057dd55eb0e7c27f8 \ No newline at end of file +ce80383f6ea9554ad1bc99e9be4b5c040a7f7e09 \ No newline at end of file
diff --git a/third_party/gradle.tar.gz.sha1 b/third_party/gradle.tar.gz.sha1 new file mode 100644 index 0000000..c411ad6 --- /dev/null +++ b/third_party/gradle.tar.gz.sha1
@@ -0,0 +1 @@ +2316517b7942b638f56f61568f10f364308b0600 \ No newline at end of file
diff --git a/third_party/gradle/gradle-8.0.tar.gz.sha1 b/third_party/gradle/gradle-8.0.tar.gz.sha1 deleted file mode 100644 index d67ee97..0000000 --- a/third_party/gradle/gradle-8.0.tar.gz.sha1 +++ /dev/null
@@ -1 +0,0 @@ -dd1f4697d211954afb3034214453c2e06769a036 \ No newline at end of file
diff --git a/third_party/gradle/gradle-8.3.tar.gz.sha1 b/third_party/gradle/gradle-8.3.tar.gz.sha1 deleted file mode 100644 index 4afa179..0000000 --- a/third_party/gradle/gradle-8.3.tar.gz.sha1 +++ /dev/null
@@ -1 +0,0 @@ -214033edc4cfc5f29f0d3ea437f8557f424a6577 \ No newline at end of file
diff --git a/third_party/gradle/gradle.tar.gz.sha1 b/third_party/gradle/gradle.tar.gz.sha1 deleted file mode 100644 index cbf0dd4..0000000 --- a/third_party/gradle/gradle.tar.gz.sha1 +++ /dev/null
@@ -1 +0,0 @@ -6b27c3955e734eafafaa3d4e35c854a57c48b427 \ No newline at end of file
diff --git a/tools/archive_smali.py b/tools/archive_smali.py index e19da70..0c877e5 100755 --- a/tools/archive_smali.py +++ b/tools/archive_smali.py
@@ -63,7 +63,7 @@ if utils.is_bot() and not utils.IsWindows(): set_rlimit_to_max() - utils.DownloadFromGoogleCloudStorage(utils.JAVA11_SHA_FILE) + utils.DownloadFromGoogleCloudStorage(utils.JAVA8_SHA_FILE) with utils.TempDir() as temp: # Resolve dry run location to support relative directories. dry_run_output = None @@ -101,11 +101,11 @@ print('Building version: %s' % version) - # Build release to local Maven repository compiling with JDK-11. + # Build release to local Maven repository compiling with JDK-8. m2 = os.path.join(temp, 'm2') os.mkdir(m2) env = os.environ.copy() - env["JAVA_HOME"] = jdk.GetJdk11Home() + env["JAVA_HOME"] = jdk.GetJdk8Home() subprocess.check_call([ './gradlew', '-Dmaven.repo.local=%s' % m2, 'release', 'test',
diff --git a/tools/create_local_maven_with_dependencies.py b/tools/create_local_maven_with_dependencies.py index 36f9d1f..c151352 100755 --- a/tools/create_local_maven_with_dependencies.py +++ b/tools/create_local_maven_with_dependencies.py
@@ -24,8 +24,8 @@ ASM_VERSION = '9.6' # When updating update tools/asmifier.py and Toolhelper as well. ESPRESSO_VERSION = '3.0.0' FASTUTIL_VERSION = '7.2.1' -KOTLIN_METADATA_VERSION = '0.9.0' -KOTLIN_VERSION = '1.9.0' +KOTLIN_METADATA_VERSION = '0.7.0' +KOTLIN_VERSION = '1.9.20' GUAVA_VERSION = '32.1.2-jre' GSON_VERSION = '2.10.1' JAVASSIST_VERSION = '3.29.2-GA' @@ -97,10 +97,10 @@ ] PLUGIN_DEPENDENCIES = [ - 'org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:4.1.0', + 'org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:pom:4.2.1', 'org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.9.10', - 'net.ltgt.errorprone:net.ltgt.errorprone.gradle.plugin:3.0.1', - 'org.spdx.sbom:org.spdx.sbom.gradle.plugin:0.4.0', + 'net.ltgt.errorprone:net.ltgt.errorprone.gradle.plugin:pom:3.0.1', + 'org.spdx.sbom:org.spdx.sbom.gradle.plugin:pom:0.4.0', # See https://github.com/FasterXML/jackson-core/issues/999. 'ch.randelshofer:fastdoubleparser:0.8.0', ]
diff --git a/tools/gradle.py b/tools/gradle.py index 76039f9..ef77a5c 100755 --- a/tools/gradle.py +++ b/tools/gradle.py
@@ -15,17 +15,16 @@ import jdk import utils -GRADLE_DIR = os.path.join(utils.REPO_ROOT, 'third_party', 'gradle') -GRADLE8_SHA1 = os.path.join(GRADLE_DIR, 'gradle-8.3.tar.gz.sha1') -GRADLE8_TGZ = os.path.join(GRADLE_DIR, 'gradle-8.3.tar.gz') +GRADLE8_SHA1 = os.path.join(utils.THIRD_PARTY, 'gradle.tar.gz.sha1') +GRADLE8_TGZ = os.path.join(utils.THIRD_PARTY, 'gradle.tar.gz') def get_gradle(): - gradle_dir = 'gradle-8.3' + gradle_dir = os.path.join(utils.THIRD_PARTY, 'gradle') if utils.IsWindows(): - return os.path.join(GRADLE_DIR, gradle_dir, 'bin', 'gradle.bat') + return os.path.join(gradle_dir, 'bin', 'gradle.bat') else: - return os.path.join(GRADLE_DIR, gradle_dir, 'bin', 'gradle') + return os.path.join(gradle_dir, 'bin', 'gradle') def ParseOptions():