Merge commit '4c921950dd521be22d536e6029034b523a705ed5' into dev-release
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/ClassAccessFlags.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/ClassAccessFlags.java new file mode 100644 index 0000000..df8e32c --- /dev/null +++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/ClassAccessFlags.java
@@ -0,0 +1,29 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.keepanno.annotations; + +/** + * Valid matches on class access flags and their negations. + * + * <p>The negated elements make it easier to express the inverse as we cannot use a "not/negation" + * operation syntactically. + */ +public enum ClassAccessFlags { + PUBLIC, + NON_PUBLIC, + PACKAGE_PRIVATE, + NON_PACKAGE_PRIVATE, + FINAL, + NON_FINAL, + INTERFACE, + NON_INTERFACE, + ABSTRACT, + NON_ABSTRACT, + SYNTHETIC, + NON_SYNTHETIC, + ANNOTATION, + NON_ANNOTATION, + ENUM, + NON_ENUM +}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/FieldAccessFlags.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/FieldAccessFlags.java new file mode 100644 index 0000000..dd5a682 --- /dev/null +++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/FieldAccessFlags.java
@@ -0,0 +1,34 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.keepanno.annotations; + +/** + * Valid matches on field access flags and their negations. + * + * <p>The negated elements make it easier to express the inverse as we cannot use a "not/negation" + * operation syntactically. + */ +public enum FieldAccessFlags { + // General member flags. + PUBLIC, + NON_PUBLIC, + PRIVATE, + NON_PRIVATE, + PROTECTED, + NON_PROTECTED, + PACKAGE_PRIVATE, + NON_PACKAGE_PRIVATE, + STATIC, + NON_STATIC, + FINAL, + NON_FINAL, + SYNTHETIC, + NON_SYNTHETIC, + // Field specific flags. + VOLATILE, + NON_VOLATILE, + TRANSIENT, + NON_TRANSIENT, + // ENUM - No PG parser support. +}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepForApi.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepForApi.java new file mode 100644 index 0000000..44756ea --- /dev/null +++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepForApi.java
@@ -0,0 +1,57 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.keepanno.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to mark a class, field or method as part of a library API surface. + * + * <p>When a class is annotated, member patterns can be used to define which members are to be kept. + * When no member patterns are specified the default pattern is to match all public and protected + * members. + * + * <p>When a member is annotated, the member patterns cannot be used as the annotated member itself + * fully defines the item to be kept (i.e., itself). + */ +@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR}) +@Retention(RetentionPolicy.CLASS) +public @interface KeepForApi { + String description() default ""; + + /** Additional targets to be kept as part of the API surface. */ + KeepTarget[] additionalTargets() default {}; + + /** + * The target kind to be kept. + * + * <p>Default kind is CLASS_AND_MEMBERS, meaning the annotated class and/or member is to be kept. + * When annotating a class this can be set to ONLY_CLASS to avoid patterns on any members. That + * can be useful when the API members are themselves explicitly annotated. + * + * <p>It is not possible to use ONLY_CLASS if annotating a member. Also, it is never valid to use + * kind ONLY_MEMBERS as the API surface must keep the class if any member it to be accessible. + */ + KeepItemKind kind() default KeepItemKind.DEFAULT; + + // Member patterns. See KeepTarget for documentation. + MemberAccessFlags[] memberAccess() default {}; + + MethodAccessFlags[] methodAccess() default {}; + + String methodName() default ""; + + String methodReturnType() default ""; + + String[] methodParameters() default {""}; + + FieldAccessFlags[] fieldAccess() default {}; + + String fieldName() default ""; + + String fieldType() default ""; +}
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 a2e9c4f..c9626f2 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
@@ -100,6 +100,21 @@ String memberFromBinding() default ""; /** + * Define the member pattern by matching on access flags. + * + * <p>Mutually exclusive with all field and method patterns as use restricts the match to both + * types of members. + */ + MemberAccessFlags[] memberAccess() default {}; + + /** + * Define the method pattern by matching on access flags. + * + * <p>Mutually exclusive with any field properties. + */ + MethodAccessFlags[] methodAccess() default {}; + + /** * Define the method-name pattern by an exact method name. * * <p>Mutually exclusive with any field properties. @@ -127,6 +142,13 @@ String[] methodParameters() default {""}; /** + * Define the field pattern by matching on field access flags. + * + * <p>Mutually exclusive with any method properties. + */ + FieldAccessFlags[] fieldAccess() default {}; + + /** * Define the field-name pattern by an exact field name. * * <p>Mutually exclusive with any method properties.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/MemberAccessFlags.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/MemberAccessFlags.java new file mode 100644 index 0000000..b3f1a50 --- /dev/null +++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/MemberAccessFlags.java
@@ -0,0 +1,27 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.keepanno.annotations; + +/** + * Valid matches on member access flags and their negations. + * + * <p>The negated elements make it easier to express the inverse as we cannot use a "not/negation" + * operation syntactically. + */ +public enum MemberAccessFlags { + PUBLIC, + NON_PUBLIC, + PROTECTED, + NON_PROTECTED, + PACKAGE_PRIVATE, + NON_PACKAGE_PRIVATE, + PRIVATE, + NON_PRIVATE, + STATIC, + NON_STATIC, + FINAL, + NON_FINAL, + SYNTHETIC, + NON_SYNTHETIC +}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/MethodAccessFlags.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/MethodAccessFlags.java new file mode 100644 index 0000000..a287365 --- /dev/null +++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/MethodAccessFlags.java
@@ -0,0 +1,40 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.keepanno.annotations; + +/** + * Valid matches on method access flags and their negations. + * + * <p>The negated elements make it easier to express the inverse as we cannot use a "not/negation" + * operation syntactically. + */ +public enum MethodAccessFlags { + // General member flags. + PUBLIC, + NON_PUBLIC, + PRIVATE, + NON_PRIVATE, + PROTECTED, + NON_PROTECTED, + PACKAGE_PRIVATE, + NON_PACKAGE_PRIVATE, + STATIC, + NON_STATIC, + FINAL, + NON_FINAL, + SYNTHETIC, + NON_SYNTHETIC, + // Method specific flags. + SYNCHRONIZED, + NON_SYNCHRONIZED, + BRIDGE, + NON_BRIDGE, + // VARARGS - No PG parser support + NATIVE, + NON_NATIVE, + ABSTRACT, + NON_ABSTRACT, + STRICT_FP, + NON_STRICT_FP +}
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 1d33cb1..73ec688 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
@@ -3,14 +3,20 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.keepanno.asm; +import com.android.tools.r8.keepanno.ast.AccessVisibility; import com.android.tools.r8.keepanno.ast.AnnotationConstants; import com.android.tools.r8.keepanno.ast.AnnotationConstants.Binding; import com.android.tools.r8.keepanno.ast.AnnotationConstants.Condition; import com.android.tools.r8.keepanno.ast.AnnotationConstants.Edge; +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; 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.Option; import com.android.tools.r8.keepanno.ast.AnnotationConstants.Target; +import com.android.tools.r8.keepanno.ast.AnnotationConstants.UsesReflection; import com.android.tools.r8.keepanno.ast.KeepBindings; import com.android.tools.r8.keepanno.ast.KeepClassReference; import com.android.tools.r8.keepanno.ast.KeepCondition; @@ -19,13 +25,16 @@ import com.android.tools.r8.keepanno.ast.KeepEdgeException; import com.android.tools.r8.keepanno.ast.KeepEdgeMetaInfo; import com.android.tools.r8.keepanno.ast.KeepExtendsPattern; +import com.android.tools.r8.keepanno.ast.KeepFieldAccessPattern; 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.KeepItemKind; 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.KeepMemberPattern; +import com.android.tools.r8.keepanno.ast.KeepMethodAccessPattern; import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern; import com.android.tools.r8.keepanno.ast.KeepMethodParametersPattern; import com.android.tools.r8.keepanno.ast.KeepMethodPattern; @@ -42,6 +51,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.function.BiPredicate; import java.util.function.Consumer; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassReader; @@ -103,6 +113,9 @@ .build(); return new UsesReflectionVisitor(parent, this::setContext, classItem); } + if (descriptor.equals(AnnotationConstants.ForApi.DESCRIPTOR)) { + return new ForApiClassVisitor(parent, this::setContext, className); + } return null; } @@ -173,6 +186,9 @@ if (descriptor.equals(AnnotationConstants.UsesReflection.DESCRIPTOR)) { return new UsesReflectionVisitor(parent, this::setContext, createItemContext()); } + if (descriptor.equals(AnnotationConstants.ForApi.DESCRIPTOR)) { + return new ForApiMemberVisitor(parent, this::setContext, createItemContext()); + } return null; } @@ -227,6 +243,9 @@ if (descriptor.equals(AnnotationConstants.UsesReflection.DESCRIPTOR)) { return new UsesReflectionVisitor(parent, this::setContext, createItemContext()); } + if (descriptor.equals(AnnotationConstants.ForApi.DESCRIPTOR)) { + return new ForApiMemberVisitor(parent, this::setContext, createItemContext()); + } return null; } } @@ -242,24 +261,31 @@ super(ASM_VERSION); } + public abstract String getAnnotationName(); + + private String errorMessagePrefix() { + return " @" + getAnnotationName() + ": "; + } + @Override public void visit(String name, Object value) { - throw new KeepEdgeException("Unexpected value in @KeepEdge: " + name + " = " + value); + throw new KeepEdgeException( + "Unexpected value in" + errorMessagePrefix() + name + " = " + value); } @Override public AnnotationVisitor visitAnnotation(String name, String descriptor) { - throw new KeepEdgeException("Unexpected annotation in @KeepEdge: " + name); + throw new KeepEdgeException("Unexpected annotation in" + errorMessagePrefix() + name); } @Override public void visitEnum(String name, String descriptor, String value) { - throw new KeepEdgeException("Unexpected enum in @KeepEdge: " + name); + throw new KeepEdgeException("Unexpected enum in" + errorMessagePrefix() + name); } @Override public AnnotationVisitor visitArray(String name) { - throw new KeepEdgeException("Unexpected array in @KeepEdge: " + name); + throw new KeepEdgeException("Unexpected array in" + errorMessagePrefix() + name); } } @@ -274,6 +300,11 @@ } @Override + public String getAnnotationName() { + return "KeepEdge"; + } + + @Override public void visit(String name, Object value) { if (name.equals(Edge.description) && value instanceof String) { metaInfoBuilder.setDescription((String) value); @@ -285,13 +316,13 @@ @Override public AnnotationVisitor visitArray(String name) { if (name.equals(Edge.bindings)) { - return new KeepBindingsVisitor(builder::setBindings); + return new KeepBindingsVisitor(getAnnotationName(), builder::setBindings); } if (name.equals(Edge.preconditions)) { - return new KeepPreconditionsVisitor(builder::setPreconditions); + return new KeepPreconditionsVisitor(getAnnotationName(), builder::setPreconditions); } if (name.equals(Edge.consequences)) { - return new KeepConsequencesVisitor(builder::setConsequences); + return new KeepConsequencesVisitor(getAnnotationName(), builder::setConsequences); } return super.visitArray(name); } @@ -302,6 +333,156 @@ } } + /** + * Parsing of @KeepForApi on a class context. + * + * <p>When used on a class context the annotation allows the member related content of a normal + * item. This parser extends the base item visitor and throws an error if any class specific + * properties are encountered. + */ + private static class ForApiClassVisitor extends KeepItemVisitorBase { + private final String className; + private final Parent<KeepEdge> parent; + private final KeepEdge.Builder builder = KeepEdge.builder(); + private final KeepConsequences.Builder consequences = KeepConsequences.builder(); + private final KeepEdgeMetaInfo.Builder metaInfoBuilder = KeepEdgeMetaInfo.builder(); + + ForApiClassVisitor( + Parent<KeepEdge> parent, Consumer<KeepEdgeMetaInfo.Builder> addContext, String className) { + this.className = className; + this.parent = parent; + addContext.accept(metaInfoBuilder); + // The class context/holder is the annotated class. + visit(Item.className, className); + // The default kind is to target the class and its members. + visitEnum(null, Kind.DESCRIPTOR, Kind.CLASS_AND_MEMBERS); + } + + @Override + public String getAnnotationName() { + return ForApi.CLASS.getSimpleName(); + } + + @Override + public void visit(String name, Object value) { + if (name.equals(Edge.description) && value instanceof String) { + metaInfoBuilder.setDescription((String) value); + return; + } + super.visit(name, value); + } + + @Override + public AnnotationVisitor visitArray(String name) { + if (name.equals(ForApi.additionalTargets)) { + return new KeepConsequencesVisitor( + getAnnotationName(), + additionalConsequences -> { + additionalConsequences.forEachTarget(consequences::addTarget); + }); + } + return super.visitArray(name); + } + + @Override + public void visitEnd() { + if (!getKind().equals(KeepItemKind.ONLY_CLASS) && isDefaultMemberDeclaration()) { + // If no member declarations have been made, set public & protected as the default. + AnnotationVisitor v = visitArray(Item.memberAccess); + v.visitEnum(null, MemberAccess.DESCRIPTOR, MemberAccess.PUBLIC); + v.visitEnum(null, MemberAccess.DESCRIPTOR, MemberAccess.PROTECTED); + } + super.visitEnd(); + KeepItemReference item = getItemReference(); + if (item.isBindingReference()) { + throw new KeepEdgeException("@KeepForApi cannot reference bindings"); + } + KeepItemPattern itemPattern = item.asItemPattern(); + String descriptor = AnnotationConstants.getDescriptorFromClassTypeName(className); + String itemDescriptor = + itemPattern.getClassReference().asClassNamePattern().getExactDescriptor(); + if (!descriptor.equals(itemDescriptor)) { + throw new KeepEdgeException("@KeepForApi must reference its class context " + className); + } + if (itemPattern.getKind().equals(KeepItemKind.ONLY_MEMBERS)) { + throw new KeepEdgeException("@KeepForApi kind must include its class"); + } + if (!itemPattern.getExtendsPattern().isAny()) { + throw new KeepEdgeException("@KeepForApi cannot define an 'extends' pattern."); + } + consequences.addTarget(KeepTarget.builder().setItemPattern(itemPattern).build()); + parent.accept( + builder + .setMetaInfo(metaInfoBuilder.build()) + .setConsequences(consequences.build()) + .build()); + } + } + + /** + * Parsing of @KeepForApi on a member context. + * + * <p>When used on a member context the annotation does not allow member related patterns. + */ + private static class ForApiMemberVisitor extends AnnotationVisitorBase { + private final Parent<KeepEdge> parent; + private final KeepEdge.Builder builder = KeepEdge.builder(); + private final KeepEdgeMetaInfo.Builder metaInfoBuilder = KeepEdgeMetaInfo.builder(); + + private final KeepConsequences.Builder consequences = KeepConsequences.builder(); + + ForApiMemberVisitor( + Parent<KeepEdge> parent, + Consumer<KeepEdgeMetaInfo.Builder> addContext, + KeepItemPattern context) { + this.parent = parent; + addContext.accept(metaInfoBuilder); + consequences.addTarget( + KeepTarget.builder() + .setItemPattern( + KeepItemPattern.builder() + .copyFrom(context) + .setKind(KeepItemKind.CLASS_AND_MEMBERS) + .build()) + .build()); + } + + @Override + public String getAnnotationName() { + return ForApi.CLASS.getSimpleName(); + } + + @Override + public void visit(String name, Object value) { + if (name.equals(Edge.description) && value instanceof String) { + metaInfoBuilder.setDescription((String) value); + return; + } + super.visit(name, value); + } + + @Override + public AnnotationVisitor visitArray(String name) { + if (name.equals(ForApi.additionalTargets)) { + return new KeepConsequencesVisitor( + getAnnotationName(), + additionalConsequences -> { + additionalConsequences.forEachTarget(consequences::addTarget); + }); + } + return super.visitArray(name); + } + + @Override + public void visitEnd() { + parent.accept( + builder + .setMetaInfo(metaInfoBuilder.build()) + .setConsequences(consequences.build()) + .build()); + } + } + private static class UsesReflectionVisitor extends AnnotationVisitorBase { private final Parent<KeepEdge> parent; private final KeepEdge.Builder builder = KeepEdge.builder(); @@ -318,6 +499,11 @@ } @Override + public String getAnnotationName() { + return UsesReflection.CLASS.getSimpleName(); + } + + @Override public void visit(String name, Object value) { if (name.equals(Edge.description) && value instanceof String) { metaInfoBuilder.setDescription((String) value); @@ -329,10 +515,11 @@ @Override public AnnotationVisitor visitArray(String name) { if (name.equals(AnnotationConstants.UsesReflection.value)) { - return new KeepConsequencesVisitor(builder::setConsequences); + return new KeepConsequencesVisitor(getAnnotationName(), builder::setConsequences); } if (name.equals(AnnotationConstants.UsesReflection.additionalPreconditions)) { return new KeepPreconditionsVisitor( + getAnnotationName(), additionalPreconditions -> { additionalPreconditions.forEach(preconditions::addCondition); }); @@ -351,14 +538,21 @@ } private static class KeepBindingsVisitor extends AnnotationVisitorBase { + private final String annotationName; private final Parent<KeepBindings> parent; private final KeepBindings.Builder builder = KeepBindings.builder(); - public KeepBindingsVisitor(Parent<KeepBindings> parent) { + public KeepBindingsVisitor(String annotationName, Parent<KeepBindings> parent) { + this.annotationName = annotationName; this.parent = parent; } @Override + public String getAnnotationName() { + return annotationName; + } + + @Override public AnnotationVisitor visitAnnotation(String name, String descriptor) { if (descriptor.equals(AnnotationConstants.Binding.DESCRIPTOR)) { return new KeepBindingVisitor(builder); @@ -373,14 +567,21 @@ } private static class KeepPreconditionsVisitor extends AnnotationVisitorBase { + private final String annotationName; private final Parent<KeepPreconditions> parent; private final KeepPreconditions.Builder builder = KeepPreconditions.builder(); - public KeepPreconditionsVisitor(Parent<KeepPreconditions> parent) { + public KeepPreconditionsVisitor(String annotationName, Parent<KeepPreconditions> parent) { + this.annotationName = annotationName; this.parent = parent; } @Override + public String getAnnotationName() { + return annotationName; + } + + @Override public AnnotationVisitor visitAnnotation(String name, String descriptor) { if (descriptor.equals(Condition.DESCRIPTOR)) { return new KeepConditionVisitor(builder::addCondition); @@ -395,14 +596,21 @@ } private static class KeepConsequencesVisitor extends AnnotationVisitorBase { + private final String annotationName; private final Parent<KeepConsequences> parent; private final KeepConsequences.Builder builder = KeepConsequences.builder(); - public KeepConsequencesVisitor(Parent<KeepConsequences> parent) { + public KeepConsequencesVisitor(String annotationName, Parent<KeepConsequences> parent) { + this.annotationName = annotationName; this.parent = parent; } @Override + public String getAnnotationName() { + return annotationName; + } + + @Override public AnnotationVisitor visitAnnotation(String name, String descriptor) { if (descriptor.equals(Target.DESCRIPTOR)) { return KeepTargetVisitor.create(builder::addTarget); @@ -419,6 +627,8 @@ private abstract static class Declaration<T> { abstract String kind(); + abstract boolean isDefault(); + abstract T getValue(); boolean tryParse(String name, Object value) { @@ -443,6 +653,11 @@ return null; } + @Override + boolean isDefault() { + return !hasDeclaration(); + } + private boolean hasDeclaration() { return declarationValue != null || declarationVisitor != null; } @@ -551,9 +766,14 @@ } private static class MethodDeclaration extends Declaration<KeepMethodPattern> { - + private final String annotationName; + private KeepMethodAccessPattern.Builder accessBuilder = null; private KeepMethodPattern.Builder builder = null; + private MethodDeclaration(String annotationName) { + this.annotationName = annotationName; + } + private KeepMethodPattern.Builder getBuilder() { if (builder == null) { builder = KeepMethodPattern.builder(); @@ -567,7 +787,15 @@ } @Override + boolean isDefault() { + return accessBuilder == null && builder == null; + } + + @Override KeepMethodPattern getValue() { + if (accessBuilder != null) { + getBuilder().setAccessPattern(accessBuilder.build()); + } return builder != null ? builder.build() : null; } @@ -593,8 +821,13 @@ @Override AnnotationVisitor tryParseArray(String name, Consumer<KeepMethodPattern> ignored) { + if (name.equals(Item.methodAccess)) { + accessBuilder = KeepMethodAccessPattern.builder(); + return new MethodAccessVisitor(annotationName, accessBuilder); + } if (name.equals(Item.methodParameters)) { return new StringArrayVisitor( + annotationName, params -> { if (Arrays.asList(Item.methodParametersDefaultValue).equals(params)) { return; @@ -612,9 +845,14 @@ } private static class FieldDeclaration extends Declaration<KeepFieldPattern> { - + private final String annotationName; + private KeepFieldAccessPattern.Builder accessBuilder = null; private KeepFieldPattern.Builder builder = null; + public FieldDeclaration(String annotationName) { + this.annotationName = annotationName; + } + private KeepFieldPattern.Builder getBuilder() { if (builder == null) { builder = KeepFieldPattern.builder(); @@ -628,7 +866,15 @@ } @Override + boolean isDefault() { + return accessBuilder == null && builder == null; + } + + @Override KeepFieldPattern getValue() { + if (accessBuilder != null) { + getBuilder().setAccessPattern(accessBuilder.build()); + } return builder != null ? builder.build() : null; } @@ -653,12 +899,28 @@ } return false; } + + @Override + AnnotationVisitor tryParseArray(String name, Consumer<KeepFieldPattern> onValue) { + if (name.equals(Item.fieldAccess)) { + accessBuilder = KeepFieldAccessPattern.builder(); + return new FieldAccessVisitor(annotationName, accessBuilder); + } + return super.tryParseArray(name, onValue); + } } private static class MemberDeclaration extends Declaration<KeepMemberPattern> { + private final String annotationName; + private KeepMemberAccessPattern.Builder accessBuilder = null; + private final MethodDeclaration methodDeclaration; + private final FieldDeclaration fieldDeclaration; - private MethodDeclaration methodDeclaration = new MethodDeclaration(); - private FieldDeclaration fieldDeclaration = new FieldDeclaration(); + MemberDeclaration(String annotationName) { + this.annotationName = annotationName; + methodDeclaration = new MethodDeclaration(annotationName); + fieldDeclaration = new FieldDeclaration(annotationName); + } @Override String kind() { @@ -666,9 +928,21 @@ } @Override + public boolean isDefault() { + return accessBuilder == null && methodDeclaration.isDefault() && fieldDeclaration.isDefault(); + } + + @Override public KeepMemberPattern getValue() { KeepMethodPattern method = methodDeclaration.getValue(); KeepFieldPattern field = fieldDeclaration.getValue(); + if (accessBuilder != null) { + if (method != null || field != null) { + throw new KeepEdgeException( + "Cannot define common member access as well as field or method pattern"); + } + return KeepMemberPattern.memberBuilder().setAccessPattern(accessBuilder.build()).build(); + } if (method != null && field != null) { throw new KeepEdgeException("Cannot define both a field and a method pattern"); } @@ -688,6 +962,10 @@ @Override AnnotationVisitor tryParseArray(String name, Consumer<KeepMemberPattern> ignored) { + if (name.equals(Item.memberAccess)) { + accessBuilder = KeepMemberAccessPattern.memberBuilder(); + return new MemberAccessVisitor(annotationName, accessBuilder); + } AnnotationVisitor visitor = methodDeclaration.tryParseArray(name, v -> {}); if (visitor != null) { return visitor; @@ -697,23 +975,32 @@ } private abstract static class KeepItemVisitorBase extends AnnotationVisitorBase { - private Parent<KeepItemReference> parent; private String memberBindingReference = null; private KeepItemKind kind = null; private final ClassDeclaration classDeclaration = new ClassDeclaration(); private final ExtendsDeclaration extendsDeclaration = new ExtendsDeclaration(); - private final MemberDeclaration memberDeclaration = new MemberDeclaration(); + private final MemberDeclaration memberDeclaration; - public KeepItemVisitorBase(Parent<KeepItemReference> parent) { - setParent(parent); + // Constructed item available once visitEnd has been called. + private KeepItemReference itemReference = null; + + KeepItemVisitorBase() { + memberDeclaration = new MemberDeclaration(getAnnotationName()); } - public KeepItemVisitorBase() {} + public KeepItemReference getItemReference() { + if (itemReference == null) { + throw new KeepEdgeException("Item reference not finalized. Missing call to visitEnd()"); + } + return itemReference; + } - void setParent(Parent<KeepItemReference> parent) { - assert parent != null; - assert this.parent == null; - this.parent = parent; + public KeepItemKind getKind() { + return kind; + } + + public boolean isDefaultMemberDeclaration() { + return memberDeclaration.isDefault(); } @Override @@ -772,7 +1059,7 @@ throw new KeepEdgeException( "Cannot define an item explicitly and via a member-binding reference"); } - parent.accept(KeepItemReference.fromBindingReference(memberBindingReference)); + itemReference = KeepItemReference.fromBindingReference(memberBindingReference); } else { KeepMemberPattern memberPattern = memberDeclaration.getValue(); // If the kind is not set (default) then the content of the members determines the kind. @@ -783,14 +1070,14 @@ if (!kind.equals(KeepItemKind.ONLY_CLASS) && memberPattern.isNone()) { memberPattern = KeepMemberPattern.allMembers(); } - parent.accept( + itemReference = KeepItemReference.fromItemPattern( KeepItemPattern.builder() .setKind(kind) .setClassReference(classDeclaration.getValue()) .setExtendsPattern(extendsDeclaration.getValue()) .setMemberPattern(memberPattern) - .build())); + .build()); } } } @@ -799,24 +1086,14 @@ private final KeepBindings.Builder builder; private String bindingName; - private KeepItemPattern item; public KeepBindingVisitor(KeepBindings.Builder builder) { this.builder = builder; - setParent( - item -> { - // The language currently disallows aliasing bindings, thus a binding should directly be - // defined by a reference to another binding. - if (item.isBindingReference()) { - throw new KeepEdgeException( - "Invalid binding reference to '" - + item.asBindingReference() - + "' in binding definition of '" - + bindingName - + "'"); - } - this.item = item.asItemPattern(); - }); + } + + @Override + public String getAnnotationName() { + return Binding.CLASS.getSimpleName(); } @Override @@ -831,20 +1108,37 @@ @Override public void visitEnd() { super.visitEnd(); - builder.addBinding(bindingName, item); + KeepItemReference item = getItemReference(); + // The language currently disallows aliasing bindings, thus a binding should directly be + // defined by a reference to another binding. + if (item.isBindingReference()) { + throw new KeepEdgeException( + "Invalid binding reference to '" + + item.asBindingReference() + + "' in binding definition of '" + + bindingName + + "'"); + } + builder.addBinding(bindingName, item.asItemPattern()); } } private static class StringArrayVisitor extends AnnotationVisitorBase { - + private final String annotationName; private final Consumer<List<String>> fn; private final List<String> strings = new ArrayList<>(); - public StringArrayVisitor(Consumer<List<String>> fn) { + public StringArrayVisitor(String annotationName, Consumer<List<String>> fn) { + this.annotationName = annotationName; this.fn = fn; } @Override + public String getAnnotationName() { + return annotationName; + } + + @Override public void visit(String name, Object value) { if (value instanceof String) { strings.add((String) value); @@ -862,6 +1156,12 @@ private static class OptionsDeclaration extends SingleDeclaration<KeepOptions> { + private final String annotationName; + + public OptionsDeclaration(String annotationName) { + this.annotationName = annotationName; + } + @Override String kind() { return "options"; @@ -881,10 +1181,12 @@ AnnotationVisitor parseArray(String name, Consumer<KeepOptions> setValue) { if (name.equals(AnnotationConstants.Target.disallow)) { return new KeepOptionsVisitor( + annotationName, options -> setValue.accept(KeepOptions.disallowBuilder().addAll(options).build())); } if (name.equals(AnnotationConstants.Target.allow)) { return new KeepOptionsVisitor( + annotationName, options -> setValue.accept(KeepOptions.allowBuilder().addAll(options).build())); } return null; @@ -893,17 +1195,22 @@ private static class KeepTargetVisitor extends KeepItemVisitorBase { - private final KeepTarget.Builder builder; - private final OptionsDeclaration optionsDeclaration = new OptionsDeclaration(); + private final Parent<KeepTarget> parent; + private final KeepTarget.Builder builder = KeepTarget.builder(); + private final OptionsDeclaration optionsDeclaration = + new OptionsDeclaration(getAnnotationName()); static KeepTargetVisitor create(Parent<KeepTarget> parent) { - KeepTarget.Builder builder = KeepTarget.builder(); - return new KeepTargetVisitor(parent, builder); + return new KeepTargetVisitor(parent); } - private KeepTargetVisitor(Parent<KeepTarget> parent, KeepTarget.Builder builder) { - super(item -> parent.accept(builder.setItemReference(item).build())); - this.builder = builder; + private KeepTargetVisitor(Parent<KeepTarget> parent) { + this.parent = parent; + } + + @Override + public String getAnnotationName() { + return Target.CLASS.getSimpleName(); } @Override @@ -914,25 +1221,51 @@ } return super.visitArray(name); } + + @Override + public void visitEnd() { + super.visitEnd(); + parent.accept(builder.setItemReference(getItemReference()).build()); + } } private static class KeepConditionVisitor extends KeepItemVisitorBase { + private final Parent<KeepCondition> parent; + public KeepConditionVisitor(Parent<KeepCondition> parent) { - super(item -> parent.accept(KeepCondition.builder().setItemReference(item).build())); + this.parent = parent; + } + + @Override + public String getAnnotationName() { + return Condition.CLASS.getSimpleName(); + } + + @Override + public void visitEnd() { + super.visitEnd(); + parent.accept(KeepCondition.builder().setItemReference(getItemReference()).build()); } } private static class KeepOptionsVisitor extends AnnotationVisitorBase { + private final String annotationName; private final Parent<Collection<KeepOption>> parent; private final Set<KeepOption> options = new HashSet<>(); - public KeepOptionsVisitor(Parent<Collection<KeepOption>> parent) { + public KeepOptionsVisitor(String annotationName, Parent<Collection<KeepOption>> parent) { + this.annotationName = annotationName; this.parent = parent; } @Override + public String getAnnotationName() { + return annotationName; + } + + @Override public void visitEnum(String ignore, String descriptor, String value) { if (!descriptor.equals(AnnotationConstants.Option.DESCRIPTOR)) { super.visitEnum(ignore, descriptor, value); @@ -967,4 +1300,156 @@ super.visitEnd(); } } + + private static class MemberAccessVisitor extends AnnotationVisitorBase { + private final String annotationName; + KeepMemberAccessPattern.BuilderBase<?, ?> builder; + + public MemberAccessVisitor( + String annotationName, KeepMemberAccessPattern.BuilderBase<?, ?> builder) { + this.annotationName = annotationName; + this.builder = builder; + } + + @Override + public String getAnnotationName() { + return annotationName; + } + + static boolean withNormalizedAccessFlag(String flag, BiPredicate<String, Boolean> fn) { + boolean allow = !flag.startsWith(MemberAccess.NEGATION_PREFIX); + return allow + ? fn.test(flag, true) + : fn.test(flag.substring(MemberAccess.NEGATION_PREFIX.length()), false); + } + + @Override + public void visitEnum(String ignore, String descriptor, String value) { + if (!descriptor.equals(AnnotationConstants.MemberAccess.DESCRIPTOR)) { + super.visitEnum(ignore, descriptor, value); + } + boolean handled = + withNormalizedAccessFlag( + value, + (flag, allow) -> { + AccessVisibility visibility = getAccessVisibilityFromString(flag); + if (visibility != null) { + builder.setAccessVisibility(visibility, allow); + return true; + } + switch (flag) { + case MemberAccess.STATIC: + builder.setStatic(allow); + return true; + case MemberAccess.FINAL: + builder.setFinal(allow); + return true; + case MemberAccess.SYNTHETIC: + builder.setSynthetic(allow); + return true; + default: + return false; + } + }); + if (!handled) { + super.visitEnum(ignore, descriptor, value); + } + } + + private AccessVisibility getAccessVisibilityFromString(String value) { + switch (value) { + case MemberAccess.PUBLIC: + return AccessVisibility.PUBLIC; + case MemberAccess.PROTECTED: + return AccessVisibility.PROTECTED; + case MemberAccess.PACKAGE_PRIVATE: + return AccessVisibility.PACKAGE_PRIVATE; + case MemberAccess.PRIVATE: + return AccessVisibility.PRIVATE; + default: + return null; + } + } + } + + private static class MethodAccessVisitor extends MemberAccessVisitor { + + KeepMethodAccessPattern.Builder builder; + + public MethodAccessVisitor(String annotationName, KeepMethodAccessPattern.Builder builder) { + super(annotationName, builder); + this.builder = builder; + } + + @Override + public void visitEnum(String ignore, String descriptor, String value) { + if (!descriptor.equals(AnnotationConstants.MethodAccess.DESCRIPTOR)) { + super.visitEnum(ignore, descriptor, value); + } + boolean handled = + withNormalizedAccessFlag( + value, + (flag, allow) -> { + switch (flag) { + case MethodAccess.SYNCHRONIZED: + builder.setSynchronized(allow); + return true; + case MethodAccess.BRIDGE: + builder.setBridge(allow); + return true; + case MethodAccess.NATIVE: + builder.setNative(allow); + return true; + case MethodAccess.ABSTRACT: + builder.setAbstract(allow); + return true; + case MethodAccess.STRICT_FP: + builder.setStrictFp(allow); + return true; + default: + return false; + } + }); + if (!handled) { + // Continue visitation with the "member" descriptor to allow matching the common values. + super.visitEnum(ignore, MemberAccess.DESCRIPTOR, value); + } + } + } + + private static class FieldAccessVisitor extends MemberAccessVisitor { + + KeepFieldAccessPattern.Builder builder; + + public FieldAccessVisitor(String annotationName, KeepFieldAccessPattern.Builder builder) { + super(annotationName, builder); + this.builder = builder; + } + + @Override + public void visitEnum(String ignore, String descriptor, String value) { + if (!descriptor.equals(AnnotationConstants.FieldAccess.DESCRIPTOR)) { + super.visitEnum(ignore, descriptor, value); + } + boolean handled = + withNormalizedAccessFlag( + value, + (flag, allow) -> { + switch (flag) { + case FieldAccess.VOLATILE: + builder.setVolatile(allow); + return true; + case FieldAccess.TRANSIENT: + builder.setTransient(allow); + return true; + default: + return false; + } + }); + if (!handled) { + // Continue visitation with the "member" descriptor to allow matching the common values. + super.visitEnum(ignore, MemberAccess.DESCRIPTOR, value); + } + } + } }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/AccessVisibility.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/AccessVisibility.java new file mode 100644 index 0000000..690bae6 --- /dev/null +++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/AccessVisibility.java
@@ -0,0 +1,45 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.keepanno.ast; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSortedSet; +import java.util.HashSet; +import java.util.Set; + +public enum AccessVisibility { + PUBLIC, + PROTECTED, + PACKAGE_PRIVATE, + PRIVATE; + + private static final ImmutableSet<AccessVisibility> ALL = ImmutableSortedSet.copyOf(values()); + + public String toSourceSyntax() { + switch (this) { + case PUBLIC: + return "public"; + case PROTECTED: + return "protected"; + case PACKAGE_PRIVATE: + throw new KeepEdgeException("No source syntax for package-private visibility."); + case PRIVATE: + return "private"; + default: + throw new KeepEdgeException("Unexpected access visibility: " + this); + } + } + + public static boolean containsAll(Set<AccessVisibility> visibilities) { + return visibilities.size() == AccessVisibility.values().length; + } + + public static Set<AccessVisibility> createSet() { + return new HashSet<>(); + } + + public static Set<AccessVisibility> all() { + return ALL; + } +}
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 68cd0df..0daeda1 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
@@ -3,12 +3,16 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.keepanno.ast; +import com.android.tools.r8.keepanno.annotations.FieldAccessFlags; import com.android.tools.r8.keepanno.annotations.KeepBinding; import com.android.tools.r8.keepanno.annotations.KeepCondition; import com.android.tools.r8.keepanno.annotations.KeepEdge; +import com.android.tools.r8.keepanno.annotations.KeepForApi; import com.android.tools.r8.keepanno.annotations.KeepItemKind; import com.android.tools.r8.keepanno.annotations.KeepOption; import com.android.tools.r8.keepanno.annotations.KeepTarget; +import com.android.tools.r8.keepanno.annotations.MemberAccessFlags; +import com.android.tools.r8.keepanno.annotations.MethodAccessFlags; /** * Utility class for referencing the various keep annotations and their structure. @@ -19,13 +23,17 @@ public final class AnnotationConstants { public static String getDescriptor(Class<?> clazz) { - return "L" + clazz.getTypeName().replace('.', '/') + ";"; + return getDescriptorFromClassTypeName(clazz.getTypeName()); } public static String getBinaryNameFromClassTypeName(String classTypeName) { return classTypeName.replace('.', '/'); } + public static String getDescriptorFromClassTypeName(String classTypeName) { + return "L" + getBinaryNameFromClassTypeName(classTypeName) + ";"; + } + public static boolean isKeepAnnotation(String descriptor, boolean visible) { if (visible) { return false; @@ -45,6 +53,14 @@ public static final String consequences = "consequences"; } + public static final class ForApi { + public static final Class<KeepForApi> CLASS = KeepForApi.class; + public static final String DESCRIPTOR = getDescriptor(CLASS); + public static final String description = "description"; + public static final String additionalTargets = "additionalTargets"; + public static final String memberAccess = "memberAccess"; + } + public static final class UsesReflection { public static final Class<com.android.tools.r8.keepanno.annotations.UsesReflection> CLASS = com.android.tools.r8.keepanno.annotations.UsesReflection.class; @@ -65,10 +81,14 @@ public static final String extendsClassName = "extendsClassName"; public static final String extendsClassConstant = "extendsClassConstant"; + public static final String memberAccess = "memberAccess"; + + public static final String methodAccess = "methodAccess"; public static final String methodName = "methodName"; public static final String methodReturnType = "methodReturnType"; public static final String methodParameters = "methodParameters"; + public static final String fieldAccess = "fieldAccess"; public static final String fieldName = "fieldName"; public static final String fieldType = "fieldType"; @@ -129,4 +149,39 @@ public static final String ACCESS_MODIFICATION = "ACCESS_MODIFICATION"; public static final String ANNOTATION_REMOVAL = "ANNOTATION_REMOVAL"; } + + public static final class MemberAccess { + public static final Class<MemberAccessFlags> CLASS = MemberAccessFlags.class; + public static final String DESCRIPTOR = getDescriptor(CLASS); + + public static final String NEGATION_PREFIX = "NON_"; + + public static final String PUBLIC = "PUBLIC"; + public static final String PROTECTED = "PROTECTED"; + public static final String PACKAGE_PRIVATE = "PACKAGE_PRIVATE"; + public static final String PRIVATE = "PRIVATE"; + + public static final String STATIC = "STATIC"; + public static final String FINAL = "FINAL"; + public static final String SYNTHETIC = "SYNTHETIC"; + } + + public static final class MethodAccess { + public static final Class<MethodAccessFlags> CLASS = MethodAccessFlags.class; + public static final String DESCRIPTOR = getDescriptor(CLASS); + + public static final String SYNCHRONIZED = "SYNCHRONIZED"; + public static final String BRIDGE = "BRIDGE"; + public static final String NATIVE = "NATIVE"; + public static final String ABSTRACT = "ABSTRACT"; + public static final String STRICT_FP = "STRICT_FP"; + } + + public static final class FieldAccess { + public static final Class<FieldAccessFlags> CLASS = FieldAccessFlags.class; + public static final String DESCRIPTOR = getDescriptor(CLASS); + + public static final String VOLATILE = "VOLATILE"; + public static final String TRANSIENT = "TRANSIENT"; + } }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java index f1ed988..dd63480 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
@@ -70,10 +70,19 @@ * METHOD_NAME_PATTERN * METHOD_PARAMETERS_PATTERN * - * METHOD_ACCESS_PATTERN ::= any + * FIELD_ACCESS_PATTERN ::= any | FIELD_ACCESS_FLAG* | (!FIELD_ACCESS_FLAG)* + * FIELD_ACCESS_FLAG ::= MEMBER_ACCESS_FLAG | volatile | transient + * + * METHOD_ACCESS_PATTERN ::= any | METHOD_ACCESS_FLAG* | (!METHOD_ACCESS_FLAG)* * METHOD_NAME_PATTERN ::= any | exact method-name * METHOD_RETURN_TYPE_PATTERN ::= void | TYPE_PATTERN * METHOD_PARAMETERS_PATTERN ::= any | none | (TYPE_PATTERN+) + * METHOD_ACCESS_FLAG + * ::= MEMBER_ACCESS_FLAG + * | synchronized | bridge | native | abstract | strict-fp + * + * MEMBER_ACCESS_FLAG + * ::= public | protected | package-private | private | static | final | synthetic * </pre> */ public final class KeepEdge {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldAccessPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldAccessPattern.java index 056430b..55c15c2 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldAccessPattern.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldAccessPattern.java
@@ -3,41 +3,108 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.keepanno.ast; -// TODO: finish this. -public abstract class KeepFieldAccessPattern { +import com.android.tools.r8.keepanno.keeprules.RulePrinter; +import com.android.tools.r8.keepanno.keeprules.RulePrintingUtils; +import java.util.Set; - public static KeepFieldAccessPattern any() { - return Any.getInstance(); +public class KeepFieldAccessPattern extends KeepMemberAccessPattern { + + private static final KeepFieldAccessPattern ANY = + new KeepFieldAccessPattern( + AccessVisibility.all(), + ModifierPattern.any(), + ModifierPattern.any(), + ModifierPattern.any(), + ModifierPattern.any(), + ModifierPattern.any()); + + public static KeepFieldAccessPattern anyFieldAccess() { + return ANY; } - public abstract boolean isAny(); + public static Builder builder() { + return new Builder(); + } - private static class Any extends KeepFieldAccessPattern { + private final ModifierPattern volatilePattern; + private final ModifierPattern transientPattern; - private static final Any INSTANCE = new Any(); + public KeepFieldAccessPattern( + Set<AccessVisibility> allowedVisibilities, + ModifierPattern staticPattern, + ModifierPattern finalPattern, + ModifierPattern volatilePattern, + ModifierPattern transientPattern, + ModifierPattern syntheticPattern) { + super(allowedVisibilities, staticPattern, finalPattern, syntheticPattern); + this.volatilePattern = volatilePattern; + this.transientPattern = transientPattern; + } - private static Any getInstance() { - return INSTANCE; - } - - @Override - public boolean isAny() { - return true; - } - - @Override - public boolean equals(Object obj) { - return this == obj; - } - - @Override - public int hashCode() { - return System.identityHashCode(this); - } - - @Override - public String toString() { + @Override + public String toString() { + if (isAny()) { return "*"; } + StringBuilder builder = new StringBuilder(); + RulePrintingUtils.printFieldAccess(RulePrinter.withoutBackReferences(builder), this); + return builder.toString(); + } + + @Override + public boolean isAny() { + return super.isAny() && volatilePattern.isAny() && transientPattern.isAny(); + } + + public ModifierPattern getVolatilePattern() { + return volatilePattern; + } + + public ModifierPattern getTransientPattern() { + return transientPattern; + } + + public static class Builder extends BuilderBase<KeepFieldAccessPattern, Builder> { + + private ModifierPattern volatilePattern = ModifierPattern.any(); + private ModifierPattern transientPattern = ModifierPattern.any(); + + private Builder() {} + + @Override + public Builder self() { + return this; + } + + @Override + public KeepFieldAccessPattern build() { + KeepFieldAccessPattern pattern = + new KeepFieldAccessPattern( + getAllowedVisibilities(), + getStaticPattern(), + getFinalPattern(), + getVolatilePattern(), + getTransientPattern(), + getSyntheticPattern()); + return pattern.isAny() ? anyFieldAccess() : pattern; + } + + public Builder setVolatile(boolean allow) { + volatilePattern = ModifierPattern.fromAllowValue(allow); + return self(); + } + + public Builder setTransient(boolean allow) { + transientPattern = ModifierPattern.fromAllowValue(allow); + return self(); + } + + public ModifierPattern getVolatilePattern() { + return volatilePattern; + } + + public ModifierPattern getTransientPattern() { + return transientPattern; + } } }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldPattern.java index dd104eb..23e27cc 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldPattern.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldPattern.java
@@ -17,7 +17,7 @@ public static class Builder { - private KeepFieldAccessPattern accessPattern = KeepFieldAccessPattern.any(); + private KeepFieldAccessPattern accessPattern = KeepFieldAccessPattern.anyFieldAccess(); private KeepFieldNamePattern namePattern = KeepFieldNamePattern.any(); private KeepFieldTypePattern typePattern = KeepFieldTypePattern.any(); @@ -72,6 +72,7 @@ return accessPattern.isAny() && namePattern.isAny() && typePattern.isAny(); } + @Override public KeepFieldAccessPattern getAccessPattern() { return accessPattern; }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberAccessPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberAccessPattern.java new file mode 100644 index 0000000..8b18105 --- /dev/null +++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberAccessPattern.java
@@ -0,0 +1,189 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.keepanno.ast; + +import com.android.tools.r8.keepanno.keeprules.RulePrinter; +import com.android.tools.r8.keepanno.keeprules.RulePrintingUtils; +import java.util.Set; + +public class KeepMemberAccessPattern { + + private static final KeepMemberAccessPattern ANY = + new KeepMemberAccessPattern( + AccessVisibility.all(), + ModifierPattern.any(), + ModifierPattern.any(), + ModifierPattern.any()); + + public static KeepMemberAccessPattern anyMemberAccess() { + return ANY; + } + + public static Builder memberBuilder() { + return new Builder(); + } + + private final Set<AccessVisibility> allowedVisibilities; + private final ModifierPattern staticPattern; + private final ModifierPattern finalPattern; + private final ModifierPattern syntheticPattern; + + public KeepMemberAccessPattern( + Set<AccessVisibility> allowedVisibilities, + ModifierPattern staticPattern, + ModifierPattern finalPattern, + ModifierPattern syntheticPattern) { + this.allowedVisibilities = allowedVisibilities; + this.staticPattern = staticPattern; + this.finalPattern = finalPattern; + this.syntheticPattern = syntheticPattern; + } + + @Override + public String toString() { + if (isAny()) { + return "*"; + } + StringBuilder builder = new StringBuilder(); + RulePrinter printer = RulePrinter.withoutBackReferences(builder); + RulePrintingUtils.printMemberAccess(printer, this); + return builder.toString(); + } + + /** True if this matches any possible access flag. */ + public boolean isAny() { + return isAnyVisibility() + && staticPattern.isAny() + && finalPattern.isAny() + && syntheticPattern.isAny(); + } + + public boolean isAnyVisibility() { + return AccessVisibility.containsAll(allowedVisibilities); + } + + public boolean isVisibilityAllowed(AccessVisibility visibility) { + return allowedVisibilities.contains(visibility); + } + + public Set<AccessVisibility> getAllowedAccessVisibilities() { + return allowedVisibilities; + } + + public ModifierPattern getStaticPattern() { + return staticPattern; + } + + public ModifierPattern getFinalPattern() { + return finalPattern; + } + + public ModifierPattern getSyntheticPattern() { + return syntheticPattern; + } + + public static class Builder extends BuilderBase<KeepMemberAccessPattern, Builder> { + + @Override + public Builder self() { + return this; + } + + @Override + public KeepMemberAccessPattern build() { + Set<AccessVisibility> allowedVisibilities = getAllowedVisibilities(); + ModifierPattern staticPattern = getStaticPattern(); + ModifierPattern finalPattern = getFinalPattern(); + ModifierPattern syntheticPattern = getSyntheticPattern(); + if (AccessVisibility.containsAll(allowedVisibilities) + && staticPattern.isAny() + && finalPattern.isAny() + && syntheticPattern.isAny()) { + return KeepMemberAccessPattern.anyMemberAccess(); + } + KeepMemberAccessPattern pattern = + new KeepMemberAccessPattern( + allowedVisibilities, staticPattern, finalPattern, syntheticPattern); + assert !pattern.isAny(); + return pattern; + } + } + + public abstract static class BuilderBase< + M extends KeepMemberAccessPattern, B extends BuilderBase<M, B>> { + private final Set<AccessVisibility> allowed = AccessVisibility.createSet(); + private final Set<AccessVisibility> disallowed = AccessVisibility.createSet(); + private ModifierPattern staticPattern = ModifierPattern.any(); + private ModifierPattern finalPattern = ModifierPattern.any(); + private ModifierPattern syntheticPattern = ModifierPattern.any(); + + BuilderBase() {} + + public abstract B self(); + + public abstract M build(); + + public B copyOfMemberAccess(KeepMemberAccessPattern accessPattern) { + allowed.clear(); + disallowed.clear(); + allowed.addAll(accessPattern.getAllowedAccessVisibilities()); + staticPattern = accessPattern.getStaticPattern(); + finalPattern = accessPattern.getFinalPattern(); + return self(); + } + + public ModifierPattern getStaticPattern() { + return staticPattern; + } + + public ModifierPattern getFinalPattern() { + return finalPattern; + } + + public ModifierPattern getSyntheticPattern() { + return syntheticPattern; + } + + public Set<AccessVisibility> getAllowedVisibilities() { + // Fast path for any visibility pattern. + if (allowed.isEmpty() && disallowed.isEmpty()) { + return AccessVisibility.all(); + } + // If no explict "allows" have been set, all visibilities are allowed. + Set<AccessVisibility> result = AccessVisibility.createSet(); + if (allowed.isEmpty()) { + result.addAll(AccessVisibility.all()); + } else { + result.addAll(allowed); + } + // Any explict disallow narrows the allowed visibilities. + result.removeAll(disallowed); + if (result.isEmpty()) { + throw new KeepEdgeException("Empty access visibility pattern will never match a member"); + } + return result; + } + + public B setAccessVisibility(AccessVisibility visibility, boolean allow) { + Set<AccessVisibility> set = allow ? allowed : disallowed; + set.add(visibility); + return self(); + } + + public B setStatic(boolean allow) { + staticPattern = ModifierPattern.fromAllowValue(allow); + return self(); + } + + public B setFinal(boolean allow) { + finalPattern = ModifierPattern.fromAllowValue(allow); + return self(); + } + + public B setSynthetic(boolean allow) { + syntheticPattern = ModifierPattern.fromAllowValue(allow); + return self(); + } + } +}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberPattern.java index d994d84..941a325 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberPattern.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberPattern.java
@@ -13,6 +13,61 @@ return All.getInstance(); } + public static Builder memberBuilder() { + return new Builder(); + } + + public static class Builder { + private KeepMemberAccessPattern accessPattern = KeepMemberAccessPattern.anyMemberAccess(); + + public Builder setAccessPattern(KeepMemberAccessPattern accessPattern) { + this.accessPattern = accessPattern; + return this; + } + + public KeepMemberPattern build() { + if (accessPattern.isAny()) { + return allMembers(); + } + return new Some(accessPattern); + } + } + + private static class Some extends KeepMemberPattern { + private final KeepMemberAccessPattern accessPattern; + + public Some(KeepMemberAccessPattern accessPattern) { + this.accessPattern = accessPattern; + } + + @Override + public KeepMemberAccessPattern getAccessPattern() { + return accessPattern; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Some some = (Some) o; + return accessPattern.equals(some.accessPattern); + } + + @Override + public int hashCode() { + return accessPattern.hashCode(); + } + + @Override + public String toString() { + return "Member{" + "access=" + accessPattern + '}'; + } + } + private static class All extends KeepMemberPattern { private static final All INSTANCE = new All(); @@ -27,6 +82,11 @@ } @Override + public KeepMemberAccessPattern getAccessPattern() { + return KeepMemberAccessPattern.anyMemberAccess(); + } + + @Override public boolean equals(Object obj) { return this == obj; } @@ -81,6 +141,10 @@ return false; } + public final boolean isGeneralMember() { + return !isNone() && !isMethod() && !isField(); + } + public final boolean isMethod() { return asMethod() != null; } @@ -96,4 +160,8 @@ public KeepFieldPattern asField() { return null; } + + public KeepMemberAccessPattern getAccessPattern() { + throw new KeepEdgeException("Invalid access to member access pattern"); + } }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodAccessPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodAccessPattern.java index 4417b2e..c8f83b2 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodAccessPattern.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodAccessPattern.java
@@ -3,42 +3,169 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.keepanno.ast; +import com.android.tools.r8.keepanno.keeprules.RulePrinter; +import com.android.tools.r8.keepanno.keeprules.RulePrintingUtils; +import java.util.Set; -// TODO: finish this. -public abstract class KeepMethodAccessPattern { +public class KeepMethodAccessPattern extends KeepMemberAccessPattern { - public static KeepMethodAccessPattern any() { - return Any.getInstance(); + private static final KeepMethodAccessPattern ANY = + new KeepMethodAccessPattern( + AccessVisibility.all(), + ModifierPattern.any(), + ModifierPattern.any(), + ModifierPattern.any(), + ModifierPattern.any(), + ModifierPattern.any(), + ModifierPattern.any(), + ModifierPattern.any(), + ModifierPattern.any()); + + public static KeepMethodAccessPattern anyMethodAccess() { + return ANY; } - public abstract boolean isAny(); + public static Builder builder() { + return new Builder(); + } - private static class Any extends KeepMethodAccessPattern { + private final ModifierPattern synchronizedPattern; + private final ModifierPattern bridgePattern; + private final ModifierPattern nativePattern; + private final ModifierPattern abstractPattern; + private final ModifierPattern strictFpPattern; - private static final Any INSTANCE = new Any(); + public KeepMethodAccessPattern( + Set<AccessVisibility> allowedVisibilities, + ModifierPattern staticPattern, + ModifierPattern finalPattern, + ModifierPattern synchronizedPattern, + ModifierPattern bridgePattern, + ModifierPattern nativePattern, + ModifierPattern abstractPattern, + ModifierPattern syntheticPattern, + ModifierPattern strictFpPattern) { + super(allowedVisibilities, staticPattern, finalPattern, syntheticPattern); + this.synchronizedPattern = synchronizedPattern; + this.bridgePattern = bridgePattern; + this.nativePattern = nativePattern; + this.abstractPattern = abstractPattern; + this.strictFpPattern = strictFpPattern; + } - private static Any getInstance() { - return INSTANCE; - } - - @Override - public boolean isAny() { - return true; - } - - @Override - public boolean equals(Object obj) { - return this == obj; - } - - @Override - public int hashCode() { - return System.identityHashCode(this); - } - - @Override - public String toString() { + @Override + public String toString() { + if (isAny()) { return "*"; } + StringBuilder builder = new StringBuilder(); + RulePrintingUtils.printMethodAccess(RulePrinter.withoutBackReferences(builder), this); + return builder.toString(); + } + + @Override + public boolean isAny() { + return super.isAny() + && synchronizedPattern.isAny() + && bridgePattern.isAny() + && nativePattern.isAny() + && abstractPattern.isAny() + && strictFpPattern.isAny(); + } + + public ModifierPattern getSynchronizedPattern() { + return synchronizedPattern; + } + + public ModifierPattern getBridgePattern() { + return bridgePattern; + } + + public ModifierPattern getNativePattern() { + return nativePattern; + } + + public ModifierPattern getAbstractPattern() { + return abstractPattern; + } + + public ModifierPattern getStrictFpPattern() { + return strictFpPattern; + } + + public static class Builder extends BuilderBase<KeepMethodAccessPattern, Builder> { + private ModifierPattern synchronizedPattern = ModifierPattern.any(); + private ModifierPattern bridgePattern = ModifierPattern.any(); + private ModifierPattern nativePattern = ModifierPattern.any(); + private ModifierPattern abstractPattern = ModifierPattern.any(); + private ModifierPattern strictFpPattern = ModifierPattern.any(); + + private Builder() {} + + @Override + public Builder self() { + return this; + } + + @Override + public KeepMethodAccessPattern build() { + KeepMethodAccessPattern pattern = + new KeepMethodAccessPattern( + getAllowedVisibilities(), + getStaticPattern(), + getFinalPattern(), + getSynchronizedPattern(), + getBridgePattern(), + getNativePattern(), + getAbstractPattern(), + getSyntheticPattern(), + getStrictFpPattern()); + return pattern.isAny() ? anyMethodAccess() : pattern; + } + + public Builder setSynchronized(boolean allow) { + synchronizedPattern = ModifierPattern.fromAllowValue(allow); + return this; + } + + public Builder setBridge(boolean allow) { + bridgePattern = ModifierPattern.fromAllowValue(allow); + return this; + } + + public Builder setNative(boolean allow) { + nativePattern = ModifierPattern.fromAllowValue(allow); + return this; + } + + public Builder setAbstract(boolean allow) { + abstractPattern = ModifierPattern.fromAllowValue(allow); + return this; + } + + public Builder setStrictFp(boolean allow) { + strictFpPattern = ModifierPattern.fromAllowValue(allow); + return this; + } + + public ModifierPattern getSynchronizedPattern() { + return synchronizedPattern; + } + + public ModifierPattern getBridgePattern() { + return bridgePattern; + } + + public ModifierPattern getNativePattern() { + return nativePattern; + } + + public ModifierPattern getAbstractPattern() { + return abstractPattern; + } + + public ModifierPattern getStrictFpPattern() { + return strictFpPattern; + } } }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java index 4104392..a7a4977 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java
@@ -12,13 +12,13 @@ return new Builder(); } - public static KeepMemberPattern allMethods() { + public static KeepMethodPattern allMethods() { return builder().build(); } public static class Builder { - private KeepMethodAccessPattern accessPattern = KeepMethodAccessPattern.any(); + private KeepMethodAccessPattern accessPattern = KeepMethodAccessPattern.anyMethodAccess(); private KeepMethodNamePattern namePattern = KeepMethodNamePattern.any(); private KeepMethodReturnTypePattern returnTypePattern = KeepMethodReturnTypePattern.any(); private KeepMethodParametersPattern parametersPattern = KeepMethodParametersPattern.any(); @@ -100,6 +100,7 @@ && parametersPattern.isAny(); } + @Override public KeepMethodAccessPattern getAccessPattern() { return accessPattern; }
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/ModifierPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/ModifierPattern.java new file mode 100644 index 0000000..77ce8ad --- /dev/null +++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/ModifierPattern.java
@@ -0,0 +1,72 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.keepanno.ast; + +/** Three-point valued matcher on an access modifier. */ +public class ModifierPattern { + + private static final ModifierPattern ANY = + new ModifierPattern() { + @Override + public boolean isAny() { + return true; + } + }; + + private static final ModifierPattern POSITIVE = + new ModifierPattern() { + @Override + public boolean isOnlyPositive() { + return true; + } + }; + + private static final ModifierPattern NEGATIVE = + new ModifierPattern() { + @Override + public boolean isOnlyNegative() { + return true; + } + }; + + public static ModifierPattern fromAllowValue(boolean allow) { + return allow ? onlyPositive() : onlyNegative(); + } + + public static ModifierPattern any() { + return ANY; + } + + public static ModifierPattern onlyPositive() { + return POSITIVE; + } + + public static ModifierPattern onlyNegative() { + return NEGATIVE; + } + + private ModifierPattern() {} + + public boolean isAny() { + return false; + } + + public boolean isOnlyPositive() { + return false; + } + + public boolean isOnlyNegative() { + return false; + } + + @Override + public boolean equals(Object obj) { + return this == obj; + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } +}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java index ee80978..7d70e08 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
@@ -8,10 +8,12 @@ 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.KeepItemPattern; import com.android.tools.r8.keepanno.ast.KeepItemReference; import com.android.tools.r8.keepanno.ast.KeepMemberPattern; +import com.android.tools.r8.keepanno.ast.KeepMethodAccessPattern; import com.android.tools.r8.keepanno.ast.KeepMethodPattern; import com.android.tools.r8.keepanno.ast.KeepOptions; import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern; @@ -349,12 +351,12 @@ List<String> nonAllMemberTargets = new ArrayList<>(targetMembers.size()); for (String targetMember : targetMembers) { KeepMemberPattern memberPattern = memberPatterns.get(targetMember); - if (memberPattern.isAllMembers() && conditionMembers.contains(targetMember)) { - // This pattern is a pattern for "any member" and it is bound by a condition. + if (memberPattern.isGeneralMember() && conditionMembers.contains(targetMember)) { + // This pattern is on "members in general" and it is bound by a condition. // Since backrefs can't reference a *-member we split this target in two, one for // fields and one for methods. HashMap<String, KeepMemberPattern> copyWithMethod = new HashMap<>(memberPatterns); - copyWithMethod.put(targetMember, KeepMethodPattern.allMethods()); + copyWithMethod.put(targetMember, copyMethodFromMember(memberPattern)); rules.add( new PgDependentMembersRule( metaInfo, @@ -365,7 +367,7 @@ Collections.singletonList(targetMember), targetKeepKind)); HashMap<String, KeepMemberPattern> copyWithField = new HashMap<>(memberPatterns); - copyWithField.put(targetMember, KeepFieldPattern.allFields()); + copyWithField.put(targetMember, copyFieldFromMember(memberPattern)); rules.add( new PgDependentMembersRule( metaInfo, @@ -394,6 +396,18 @@ }); } + private static KeepMethodPattern copyMethodFromMember(KeepMemberPattern pattern) { + KeepMethodAccessPattern accessPattern = + KeepMethodAccessPattern.builder().copyOfMemberAccess(pattern.getAccessPattern()).build(); + return KeepMethodPattern.builder().setAccessPattern(accessPattern).build(); + } + + private static KeepFieldPattern copyFieldFromMember(KeepMemberPattern pattern) { + KeepFieldAccessPattern accessPattern = + KeepFieldAccessPattern.builder().copyOfMemberAccess(pattern.getAccessPattern()).build(); + return KeepFieldPattern.builder().setAccessPattern(accessPattern).build(); + } + private static KeepQualifiedClassNamePattern getClassNamePattern( KeepItemPattern itemPattern, KeepBindings bindings) { return itemPattern.getClassReference().isClassNamePattern()
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrintingUtils.java b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrintingUtils.java index 6f08712..8084164 100644 --- a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrintingUtils.java +++ b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/RulePrintingUtils.java
@@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.keepanno.keeprules; +import com.android.tools.r8.keepanno.ast.AccessVisibility; import com.android.tools.r8.keepanno.ast.KeepClassReference; import com.android.tools.r8.keepanno.ast.KeepEdgeException; import com.android.tools.r8.keepanno.ast.KeepEdgeMetaInfo; @@ -11,6 +12,7 @@ import com.android.tools.r8.keepanno.ast.KeepFieldNamePattern; import com.android.tools.r8.keepanno.ast.KeepFieldPattern; import com.android.tools.r8.keepanno.ast.KeepItemPattern; +import com.android.tools.r8.keepanno.ast.KeepMemberAccessPattern; import com.android.tools.r8.keepanno.ast.KeepMemberPattern; import com.android.tools.r8.keepanno.ast.KeepMethodAccessPattern; import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern; @@ -23,8 +25,10 @@ import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern; import com.android.tools.r8.keepanno.ast.KeepTypePattern; import com.android.tools.r8.keepanno.ast.KeepUnqualfiedClassNamePattern; +import com.android.tools.r8.keepanno.ast.ModifierPattern; import com.android.tools.r8.keepanno.utils.Unimplemented; import java.util.List; +import java.util.Set; import java.util.function.BiConsumer; public abstract class RulePrintingUtils { @@ -110,11 +114,15 @@ if (member.isField()) { return printField(member.asField(), printer); } - throw new Unimplemented(); + // The pattern is a restricted member pattern, e.g., it must apply to fields and methods + // without any specifics not common to both. For now that is just the access pattern. + assert !member.getAccessPattern().isAny(); + printMemberAccess(printer, member.getAccessPattern()); + return printer.appendWithoutBackReferenceAssert("*").append(";"); } private static RulePrinter printField(KeepFieldPattern fieldPattern, RulePrinter builder) { - printFieldAccess(builder, " ", fieldPattern.getAccessPattern()); + printFieldAccess(builder, fieldPattern.getAccessPattern()); printType(builder, fieldPattern.getTypePattern().asType()); builder.append(" "); printFieldName(builder, fieldPattern.getNamePattern()); @@ -122,7 +130,7 @@ } private static RulePrinter printMethod(KeepMethodPattern methodPattern, RulePrinter builder) { - printMethodAccess(builder, " ", methodPattern.getAccessPattern()); + printMethodAccess(builder, methodPattern.getAccessPattern()); printReturnType(builder, methodPattern.getReturnTypePattern()); builder.append(" "); printMethodName(builder, methodPattern.getNamePattern()); @@ -174,24 +182,69 @@ return builder.append(descriptorToJavaType(typePattern.getDescriptor())); } - private static RulePrinter printMethodAccess( - RulePrinter builder, String indent, KeepMethodAccessPattern accessPattern) { + public static RulePrinter printMemberAccess( + RulePrinter printer, KeepMemberAccessPattern accessPattern) { if (accessPattern.isAny()) { // No text will match any access pattern. // Don't print the indent in this case. - return builder; + return printer; } - throw new Unimplemented(); + printVisibilityModifiers(printer, accessPattern); + printModifier(printer, accessPattern.getStaticPattern(), "static"); + printModifier(printer, accessPattern.getFinalPattern(), "final"); + printModifier(printer, accessPattern.getSyntheticPattern(), "synthetic"); + return printer; } - private static RulePrinter printFieldAccess( - RulePrinter builder, String indent, KeepFieldAccessPattern accessPattern) { - if (accessPattern.isAny()) { - // No text will match any access pattern. - // Don't print the indent in this case. - return builder; + public static void printVisibilityModifiers( + RulePrinter printer, KeepMemberAccessPattern accessPattern) { + if (accessPattern.isAnyVisibility()) { + return; } - throw new Unimplemented(); + Set<AccessVisibility> allowed = accessPattern.getAllowedAccessVisibilities(); + // Package private does not have an actual representation it must be matched by its absence. + // Thus, in the case of package-private the match is the negation of those not-present. + boolean negated = allowed.contains(AccessVisibility.PACKAGE_PRIVATE); + for (AccessVisibility visibility : AccessVisibility.values()) { + if (!visibility.equals(AccessVisibility.PACKAGE_PRIVATE)) { + if (!negated == allowed.contains(visibility)) { + if (negated) { + printer.append("!"); + } + printer.append(visibility.toSourceSyntax()).append(" "); + } + } + } + } + + public static void printModifier( + RulePrinter printer, ModifierPattern modifierPattern, String syntax) { + if (modifierPattern.isAny()) { + return; + } + if (modifierPattern.isOnlyNegative()) { + printer.append("!"); + } + printer.append(syntax).append(" "); + } + + public static RulePrinter printMethodAccess( + RulePrinter printer, KeepMethodAccessPattern accessPattern) { + printMemberAccess(printer, accessPattern); + printModifier(printer, accessPattern.getSynchronizedPattern(), "synchronized"); + printModifier(printer, accessPattern.getBridgePattern(), "bridge"); + printModifier(printer, accessPattern.getNativePattern(), "native"); + printModifier(printer, accessPattern.getAbstractPattern(), "abstract"); + printModifier(printer, accessPattern.getStrictFpPattern(), "strictfp"); + return printer; + } + + public static RulePrinter printFieldAccess( + RulePrinter printer, KeepFieldAccessPattern accessPattern) { + printMemberAccess(printer, accessPattern); + RulePrintingUtils.printModifier(printer, accessPattern.getVolatilePattern(), "volatile"); + RulePrintingUtils.printModifier(printer, accessPattern.getTransientPattern(), "transient"); + return printer; } public static RulePrinter printClassName(
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java index a233e49..4d1269c 100644 --- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java +++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -21,6 +21,7 @@ import com.android.tools.r8.utils.DumpInputFlags; import com.android.tools.r8.utils.FileUtils; import com.android.tools.r8.utils.InternalOptions.DesugarState; +import com.android.tools.r8.utils.ProgramConsumerUtils; import com.android.tools.r8.utils.Reporter; import com.android.tools.r8.utils.ThreadUtils; import java.nio.file.Path; @@ -147,6 +148,7 @@ void dumpBaseCommandOptions(DumpOptions.Builder builder) { builder + .setBackend(ProgramConsumerUtils.getBackend(programConsumer)) .setCompilationMode(getMode()) .setMinApi(getMinApiLevel()) .setOptimizeMultidexForLinearAlloc(isOptimizeMultidexForLinearAlloc())
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java index 7508aa1..55438d2 100644 --- a/src/main/java/com/android/tools/r8/D8.java +++ b/src/main/java/com/android/tools/r8/D8.java
@@ -201,7 +201,7 @@ System.out.println("D8 is running with free memory:" + runtime.freeMemory()); System.out.println("D8 is running with max memory:" + runtime.maxMemory()); } - Timing timing = Timing.create("D8", options); + Timing timing = Timing.create("D8 " + Version.LABEL, options); try { timing.begin("Pre conversion"); // Synthetic assertion to check that testing assertions works and can be enabled. @@ -436,7 +436,7 @@ return finalDexApp.build(); } - static class ConvertedCfFiles implements DexIndexedConsumer, ProgramResourceProvider { + public static class ConvertedCfFiles implements DexIndexedConsumer, ProgramResourceProvider { private final List<ProgramResource> resources = new ArrayList<>();
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java index 7d1c9bd..3c17174 100644 --- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java +++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -15,6 +15,7 @@ import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.SubtypingInfo; +import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions; import com.android.tools.r8.shaking.Enqueuer; import com.android.tools.r8.shaking.EnqueuerFactory; import com.android.tools.r8.shaking.MainDexInfo; @@ -65,8 +66,12 @@ SubtypingInfo subtypingInfo = SubtypingInfo.create(appView); + ArtProfileCollectionAdditions artProfileCollectionAdditions = + ArtProfileCollectionAdditions.nop(); MainDexRootSet mainDexRootSet = - MainDexRootSet.builder(appView, subtypingInfo, options.mainDexKeepRules).build(executor); + MainDexRootSet.builder( + appView, artProfileCollectionAdditions, subtypingInfo, options.mainDexKeepRules) + .build(executor); appView.setMainDexRootSet(mainDexRootSet); GraphConsumer graphConsumer = options.mainDexKeptGraphConsumer;
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java index 97467f1..fc7bb85 100644 --- a/src/main/java/com/android/tools/r8/R8.java +++ b/src/main/java/com/android/tools/r8/R8.java
@@ -163,7 +163,7 @@ if (options.printMemory) { System.gc(); } - timing = Timing.create("R8", options); + timing = Timing.create("R8 " + Version.LABEL, options); } /** @@ -303,13 +303,11 @@ appView.dexItemFactory()); // Upfront desugaring generation: Generates new program classes to be added in the app. - ArtProfileCollectionAdditions artProfileCollectionAdditions = - ArtProfileCollectionAdditions.create(appView); CfClassSynthesizerDesugaringEventConsumer classSynthesizerEventConsumer = - CfClassSynthesizerDesugaringEventConsumer.create(artProfileCollectionAdditions); + CfClassSynthesizerDesugaringEventConsumer.createForR8(appView); CfClassSynthesizerDesugaringCollection.create(appView) .synthesizeClasses(executorService, classSynthesizerEventConsumer); - artProfileCollectionAdditions.commit(appView); + classSynthesizerEventConsumer.finished(appView); if (appView.getSyntheticItems().hasPendingSyntheticClasses()) { appView.setAppInfo( appView @@ -333,11 +331,14 @@ options.itemFactory, options.getMinApiLevel())); } } + ArtProfileCollectionAdditions artProfileCollectionAdditions = + ArtProfileCollectionAdditions.create(appView); AssumeInfoCollection.Builder assumeInfoCollectionBuilder = AssumeInfoCollection.builder(); SubtypingInfo subtypingInfo = SubtypingInfo.create(appView); appView.setRootSet( RootSet.builder( appView, + artProfileCollectionAdditions, subtypingInfo, Iterables.concat( options.getProguardConfiguration().getRules(), synthesizedProguardRules)) @@ -351,7 +352,11 @@ assert appView.graphLens().isIdentityLens(); // Find classes which may have code executed before secondary dex files installation. MainDexRootSet mainDexRootSet = - MainDexRootSet.builder(appView, subtypingInfo, options.mainDexKeepRules) + MainDexRootSet.builder( + appView, + artProfileCollectionAdditions, + subtypingInfo, + options.mainDexKeepRules) .build(executorService); appView.setMainDexRootSet(mainDexRootSet); appView.appInfo().unsetObsolete(); @@ -366,6 +371,7 @@ annotationRemoverBuilder, executorService, appView, + artProfileCollectionAdditions, subtypingInfo, classMergingEnqueuerExtensionBuilder); timing.end(); @@ -473,7 +479,7 @@ // should therefore be run after the publicizer. new NestReducer(appViewWithLiveness).run(executorService, timing); - appView.setGraphLens(new MemberRebindingAnalysis(appViewWithLiveness).run(executorService)); + new MemberRebindingAnalysis(appViewWithLiveness).run(executorService); appView.appInfo().withLiveness().getFieldAccessInfoCollection().restrictToProgram(appView); boolean isKotlinLibraryCompilationWithInlinePassThrough = @@ -961,12 +967,14 @@ AnnotationRemover.Builder annotationRemoverBuilder, ExecutorService executorService, AppView<AppInfoWithClassHierarchy> appView, + ArtProfileCollectionAdditions artProfileCollectionAdditions, SubtypingInfo subtypingInfo, RuntimeTypeCheckInfo.Builder classMergingEnqueuerExtensionBuilder) throws ExecutionException { timing.begin("Set up enqueuer"); Enqueuer enqueuer = - EnqueuerFactory.createForInitialTreeShaking(appView, executorService, subtypingInfo); + EnqueuerFactory.createForInitialTreeShaking( + appView, artProfileCollectionAdditions, executorService, subtypingInfo); enqueuer.setAnnotationRemoverBuilder(annotationRemoverBuilder); if (appView.options().enableInitializedClassesInInstanceMethodsAnalysis) { enqueuer.registerAnalysis(new InitializedClassesInInstanceMethodsAnalysis(appView)); @@ -986,6 +994,7 @@ timing.begin("Trace application"); EnqueuerResult enqueuerResult = enqueuer.traceApplication(appView.rootSet(), executorService, timing); + assert artProfileCollectionAdditions.verifyIsCommitted(); timing.end(); timing.begin("Finalize enqueuer result"); AppView<AppInfoWithLiveness> appViewWithLiveness =
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java index b4cca13..ee35e7f 100644 --- a/src/main/java/com/android/tools/r8/R8Command.java +++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -700,15 +700,17 @@ Version.getPatchVersion()) : fakeCompilerVersion; if (compilerVersion.getMajor() < 0) { - compilerVersion = SemanticVersion.parse(Version.ACTIVE_DEV_VERSION); + compilerVersion = + SemanticVersion.create( + Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); reporter.warning( "Running R8 version " + Version.getVersionString() - + " which cannot be represented as a semantic version. Using" - + " version " - + compilerVersion - + " for selecting Proguard configurations embedded under" - + " META-INF/"); + + ", which cannot be represented as a semantic version. Using" + + " an artificial version newer than any known version for selecting" + + " Proguard configurations embedded under META-INF/. This means that" + + " all rules with a '-max-' qualifier will be excluded and all rules" + + " with a -min- qualifier will be included."); } return compilerVersion; });
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java index 4138faf..33388ee 100644 --- a/src/main/java/com/android/tools/r8/Version.java +++ b/src/main/java/com/android/tools/r8/Version.java
@@ -15,11 +15,6 @@ // Therefore, changing this field could break our release scripts. public static final String LABEL = "8.1.19-dev"; - // The prefix of the active dev version line being worked on from this branch. This is used in the - // few cases where the compiler makes decisions based in the compiler version and where version - // 'main' cannot be used. - public static final String ACTIVE_DEV_VERSION = "8.1.0"; - private Version() { }
diff --git a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubberEventConsumer.java b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubberEventConsumer.java index ac79346..7c42536 100644 --- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubberEventConsumer.java +++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubberEventConsumer.java
@@ -7,9 +7,7 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexLibraryClass; import com.android.tools.r8.graph.DexProgramClass; -import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions; import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingApiReferenceStubberEventConsumer; -import com.android.tools.r8.profile.art.rewriting.ConcreteArtProfileCollectionAdditions; public interface ApiReferenceStubberEventConsumer { @@ -23,20 +21,7 @@ boolean isEmpty(); static ApiReferenceStubberEventConsumer create(AppView<?> appView) { - if (appView.options().getArtProfileOptions().isIncludingApiReferenceStubs()) { - ArtProfileCollectionAdditions artProfileCollectionAdditions = - ArtProfileCollectionAdditions.create(appView); - if (!artProfileCollectionAdditions.isNop()) { - return create(artProfileCollectionAdditions.asConcrete()); - } - } - return empty(); - } - - static ApiReferenceStubberEventConsumer create( - ConcreteArtProfileCollectionAdditions artProfileCollectionAdditions) { - return ArtProfileRewritingApiReferenceStubberEventConsumer.attach( - artProfileCollectionAdditions, empty()); + return ArtProfileRewritingApiReferenceStubberEventConsumer.attach(appView, empty()); } static EmptyApiReferenceStubberEventConsumer empty() {
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java index 4300df0..99cb304 100644 --- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java +++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -7,6 +7,7 @@ import com.android.tools.r8.ByteBufferProvider; import com.android.tools.r8.ByteDataView; +import com.android.tools.r8.D8.ConvertedCfFiles; import com.android.tools.r8.DataDirectoryResource; import com.android.tools.r8.DataEntryResource; import com.android.tools.r8.DataResourceConsumer; @@ -432,7 +433,9 @@ // Fail if there are pending errors, e.g., the program consumers may have reported errors. options.reporter.failIfPendingErrors(); // Supply info to all additional resource consumers. - supplyAdditionalConsumers(appView); + if (!(programConsumer instanceof ConvertedCfFiles)) { + supplyAdditionalConsumers(appView); + } } finally { timing.end(); }
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java index 77f653a..68f379f 100644 --- a/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java +++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriterExperimental.java
@@ -15,6 +15,7 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexString; import com.android.tools.r8.graph.ObjectToOffsetMapping; +import com.android.tools.r8.utils.BitUtils; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.ListUtils; import com.android.tools.r8.utils.ThreadUtils; @@ -146,6 +147,7 @@ } offset = section.getLayout().getEndOfFile(); + assert BitUtils.isAligned(4, offset); sections.add(section); fileTiming.end(); timings.add(fileTiming); @@ -192,7 +194,32 @@ List<MapItem> mapItems = section .getLayout() - .generateMapInfo(section.getFileWriter(), stringIdsSize, stringIdsOffset); + .generateMapInfo( + section.getFileWriter(), + section.getLayout().headerOffset, + stringIdsSize, + stringIdsOffset, + lastSection.getLayout().getStringDataOffsets()); + int originalSize = dexOutputBuffer.getInt(); + int size = 0; + for (MapItem mapItem : mapItems) { + size += mapItem.write(dexOutputBuffer); + } + assert originalSize == size; + // Calculate signature and checksum after the map is written. + section.getFileWriter().writeSignature(section.getLayout(), dexOutputBuffer); + section.getFileWriter().writeChecksum(section.getLayout(), dexOutputBuffer); + } else { + dexOutputBuffer.moveTo(section.getLayout().getMapOffset()); + List<MapItem> mapItems = + section + .getLayout() + .generateMapInfo( + section.getFileWriter(), + section.getLayout().headerOffset, + stringIdsSize, + stringIdsOffset, + lastSection.getLayout().getStringDataOffsets()); int originalSize = dexOutputBuffer.getInt(); int size = 0; for (MapItem mapItem : mapItems) { @@ -214,6 +241,7 @@ DexOutputBuffer outputBuffer, boolean last) { assert !virtualFile.isEmpty(); + assert BitUtils.isAligned(4, offset); printItemUseInfo(virtualFile); timing.begin("Reindex for lazy strings");
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java index 6d43e68..86182c2 100644 --- a/src/main/java/com/android/tools/r8/dex/FileWriter.java +++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -795,6 +795,12 @@ private byte[] dexVersionBytes() { if (options.testing.dexContainerExperiment) { + return DexVersion.V41.getBytes(); + } + // TODO(b/269089718): Remove this testing option and always emit DEX version 040 if DEX contains + // identifiers with whitespace. + if (options.testing.dexVersion40FromApiLevel30 + && options.getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.R)) { return DexVersion.V40.getBytes(); } return options.testing.forceDexVersionBytes != null @@ -809,7 +815,7 @@ dest.putByte(Constants.DEX_FILE_MAGIC_SUFFIX); // Leave out checksum and signature for now. dest.moveTo(layout.headerOffset + Constants.FILE_SIZE_OFFSET); - dest.putInt(layout.getEndOfFile()); + dest.putInt(layout.getEndOfFile() - layout.headerOffset); dest.putInt(Constants.TYPE_HEADER_ITEM_SIZE); dest.putInt(Constants.ENDIAN_CONSTANT); dest.putInt(0); @@ -912,7 +918,7 @@ } public int size() { - return length == 0 ? 0 : 1; + return length == 0 && type != Constants.TYPE_STRING_DATA_ITEM ? 0 : 1; } } @@ -1123,13 +1129,21 @@ public List<MapItem> generateMapInfo(FileWriter fileWriter) { return generateMapInfo( - fileWriter, fileWriter.mixedSectionOffsets.getStringData().size(), stringIdsOffset); + fileWriter, + 0, + fileWriter.mixedSectionOffsets.getStringData().size(), + stringIdsOffset, + getStringDataOffsets()); } public List<MapItem> generateMapInfo( - FileWriter fileWriter, int stringIdsSize, int stringIdsOffset) { + FileWriter fileWriter, + int headerOffset, + int stringIdsSize, + int stringIdsOffset, + int stringDataOffsets) { List<MapItem> mapItems = new ArrayList<>(); - mapItems.add(new MapItem(Constants.TYPE_HEADER_ITEM, 0, 1)); + mapItems.add(new MapItem(Constants.TYPE_HEADER_ITEM, headerOffset, 1)); mapItems.add( new MapItem( Constants.TYPE_STRING_ID_ITEM, @@ -1182,8 +1196,8 @@ mapItems.add( new MapItem( Constants.TYPE_STRING_DATA_ITEM, - getStringDataOffsets(), - getStringDataOffsets() == 0 ? 0 : stringIdsSize)); + stringDataOffsets, + stringDataOffsets == 0 ? 0 : stringIdsSize)); mapItems.add( new MapItem( Constants.TYPE_ANNOTATION_ITEM,
diff --git a/src/main/java/com/android/tools/r8/dump/DumpOptions.java b/src/main/java/com/android/tools/r8/dump/DumpOptions.java index ef3a443..2ecf1d3 100644 --- a/src/main/java/com/android/tools/r8/dump/DumpOptions.java +++ b/src/main/java/com/android/tools/r8/dump/DumpOptions.java
@@ -5,6 +5,7 @@ package com.android.tools.r8.dump; import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.dex.Marker.Backend; import com.android.tools.r8.dex.Marker.Tool; import com.android.tools.r8.features.FeatureSplitConfiguration; import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification; @@ -21,6 +22,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.TreeMap; @SuppressWarnings("OptionalUsedAsFieldOrParameterType") public class DumpOptions { @@ -28,6 +30,7 @@ // The following keys and values should not be changed to keep the dump utility backward // compatible with previous versions. They are also used by the python script compileDump and // the corresponding CompileDumpCompatR8 java class. + private static final String BACKEND_KEY = "backend"; private static final String TOOL_KEY = "tool"; private static final String MODE_KEY = "mode"; private static final String DEBUG_MODE_VALUE = "debug"; @@ -42,12 +45,13 @@ private static final String TREE_SHAKING_KEY = "tree-shaking"; private static final String MINIFICATION_KEY = "minification"; private static final String FORCE_PROGUARD_COMPATIBILITY_KEY = "force-proguard-compatibility"; - private static final String SYSTEM_PROPERTY_PREFIX = "system-property-"; + public static final String SYSTEM_PROPERTY_PREFIX = "system-property-"; private static final String ENABLE_MISSING_LIBRARY_API_MODELING = "enable-missing-library-api-modeling"; private static final String ANDROID_PLATFORM_BUILD = "android-platform-build"; private static final String TRACE_REFERENCES_CONSUMER = "trace_references_consumer"; + private final Backend backend; private final Tool tool; private final CompilationMode compilationMode; private final int minApi; @@ -78,6 +82,7 @@ private final boolean dumpInputToFile; private DumpOptions( + Backend backend, Tool tool, CompilationMode compilationMode, int minAPI, @@ -99,6 +104,7 @@ Map<String, String> systemProperties, boolean dumpInputToFile, String traceReferencesConsumer) { + this.backend = backend; this.tool = tool; this.compilationMode = compilationMode; this.minApi = minAPI; @@ -137,6 +143,7 @@ } if (tool != Tool.TraceReferences) { // We keep the following values for backward compatibility. + addDumpEntry(buildProperties, BACKEND_KEY, backend.name()); addDumpEntry( buildProperties, MODE_KEY, @@ -183,6 +190,9 @@ private static void parseKeyValue(Builder builder, String key, String value) { switch (key) { + case BACKEND_KEY: + builder.setBackend(Backend.valueOf(value)); + return; case TOOL_KEY: builder.setTool(Tool.valueOf(value)); return; @@ -303,6 +313,8 @@ } public static class Builder { + // Initialize backend to DEX for backwards compatibility. + private Backend backend = Backend.DEX; private Tool tool; private CompilationMode compilationMode; private int minApi; @@ -333,6 +345,11 @@ public Builder() {} + public Builder setBackend(Backend backend) { + this.backend = backend; + return this; + } + public Builder setTool(Tool tool) { this.tool = tool; return this; @@ -442,21 +459,29 @@ } public Builder readCurrentSystemProperties() { + getCurrentSystemProperties().forEach(this::setSystemProperty); + return this; + } + + public static Map<String, String> getCurrentSystemProperties() { + Map<String, String> systemProperties = new TreeMap<>(); System.getProperties() .stringPropertyNames() .forEach( name -> { if (name.startsWith("com.android.tools.r8.")) { String value = System.getProperty(name); - setSystemProperty(name, value); + systemProperties.put(name, value); } }); - return this; + return systemProperties; } public DumpOptions build() { assert tool != null; + assert tool == Tool.TraceReferences || backend != null; return new DumpOptions( + backend, tool, compilationMode, minApi,
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java index b5fe77a..9560129 100644 --- a/src/main/java/com/android/tools/r8/graph/AppView.java +++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -916,10 +916,19 @@ appView .withLiveness() .setAppInfo(appView.appInfoWithLiveness().rewrittenWithLens(application, lens)); + } else { + assert appView.hasClassHierarchy(); + AppView<AppInfoWithClassHierarchy> appViewWithClassHierarchy = + appView.withClassHierarchy(); + AppInfoWithClassHierarchy appInfo = appViewWithClassHierarchy.appInfo(); + MainDexInfo rewrittenMainDexInfo = + appInfo.getMainDexInfo().rewrittenWithLens(appView.getSyntheticItems(), lens); + appViewWithClassHierarchy.setAppInfo( + appInfo.rebuildWithMainDexInfo(rewrittenMainDexInfo)); } appView.setAppServices(appView.appServices().rewrittenWithLens(lens)); appView.setArtProfileCollection( - appView.getArtProfileCollection().rewrittenWithLens(lens)); + appView.getArtProfileCollection().rewrittenWithLens(appView, lens)); appView.setAssumeInfoCollection( appView.getAssumeInfoCollection().rewrittenWithLens(appView, lens)); if (appView.hasInitClassLens()) { @@ -948,7 +957,8 @@ boolean changed = appView.setGraphLens(lens); assert changed; - appView.setArtProfileCollection(appView.getArtProfileCollection().rewrittenWithLens(lens)); + appView.setArtProfileCollection( + appView.getArtProfileCollection().rewrittenWithLens(appView, lens)); } public void setAlreadyLibraryDesugared(Set<DexType> alreadyLibraryDesugared) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java index aff4bff..e24a22c 100644 --- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java +++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -3,6 +3,8 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.graph; +import com.android.tools.r8.androidapi.AndroidApiLevelCompute; +import com.android.tools.r8.androidapi.ComputedApiLevel; import com.android.tools.r8.dex.IndexedItemCollection; import com.android.tools.r8.dex.MixedSectionCollection; import com.android.tools.r8.graph.DexValue.DexValueAnnotation; @@ -15,6 +17,7 @@ import com.android.tools.r8.ir.desugar.CovariantReturnTypeAnnotationTransformer; import com.android.tools.r8.synthesis.SyntheticItems; import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind; +import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.Pair; import com.android.tools.r8.utils.structural.StructuralItem; @@ -56,6 +59,9 @@ public final int visibility; public final DexEncodedAnnotation annotation; + private static final int UNKNOWN_API_LEVEL = -1; + private static final int NOT_SET_API_LEVEL = -2; + private static void specify(StructuralSpecification<DexAnnotation, ?> spec) { spec.withItem(a -> a.annotation).withInt(a -> a.visibility); } @@ -469,26 +475,37 @@ } public static DexAnnotation createAnnotationSynthesizedClass( - SyntheticKind kind, DexItemFactory dexItemFactory) { + SyntheticKind kind, DexItemFactory dexItemFactory, ComputedApiLevel computedApiLevel) { DexString versionHash = dexItemFactory.createString(dexItemFactory.getSyntheticNaming().getVersionHash()); DexAnnotationElement kindElement = new DexAnnotationElement(dexItemFactory.kindString, DexValueInt.create(kind.getId())); DexAnnotationElement versionHashElement = new DexAnnotationElement(dexItemFactory.versionHashString, new DexValueString(versionHash)); - DexAnnotationElement[] elements = new DexAnnotationElement[] {kindElement, versionHashElement}; + int apiLevel = getApiLevelForSerialization(computedApiLevel); + DexAnnotationElement apiLevelElement = + new DexAnnotationElement(dexItemFactory.apiLevelString, DexValueInt.create(apiLevel)); + DexAnnotationElement[] elements = + new DexAnnotationElement[] {apiLevelElement, kindElement, versionHashElement}; return new DexAnnotation( VISIBILITY_BUILD, new DexEncodedAnnotation(dexItemFactory.annotationSynthesizedClass, elements)); } public static boolean hasSynthesizedClassAnnotation( - DexAnnotationSet annotations, DexItemFactory factory, SyntheticItems synthetics) { - return getSynthesizedClassAnnotationInfo(annotations, factory, synthetics) != null; + DexAnnotationSet annotations, + DexItemFactory factory, + SyntheticItems synthetics, + AndroidApiLevelCompute apiLevelCompute) { + return getSynthesizedClassAnnotationInfo(annotations, factory, synthetics, apiLevelCompute) + != null; } - public static SyntheticKind getSynthesizedClassAnnotationInfo( - DexAnnotationSet annotations, DexItemFactory factory, SyntheticItems synthetics) { + public static SynthesizedAnnotationClassInfo getSynthesizedClassAnnotationInfo( + DexAnnotationSet annotations, + DexItemFactory factory, + SyntheticItems synthetics, + AndroidApiLevelCompute apiLevelCompute) { if (annotations.size() != 1) { return null; } @@ -497,12 +514,13 @@ return null; } int length = annotation.annotation.elements.length; - if (length != 2) { + if (length != 3) { return null; } assert factory.kindString.isLessThan(factory.versionHashString); - DexAnnotationElement kindElement = annotation.annotation.elements[0]; - DexAnnotationElement versionHashElement = annotation.annotation.elements[1]; + DexAnnotationElement apiLevelElement = annotation.annotation.elements[0]; + DexAnnotationElement kindElement = annotation.annotation.elements[1]; + DexAnnotationElement versionHashElement = annotation.annotation.elements[2]; if (kindElement.name != factory.kindString) { return null; } @@ -515,14 +533,43 @@ if (!versionHashElement.value.isDexValueString()) { return null; } + if (apiLevelElement.name != factory.apiLevelString || !apiLevelElement.value.isDexValueInt()) { + return null; + } String currentVersionHash = synthetics.getNaming().getVersionHash(); String syntheticVersionHash = versionHashElement.value.asDexValueString().getValue().toString(); if (!currentVersionHash.equals(syntheticVersionHash)) { return null; } - SyntheticKind kind = + int apiLevelValue = apiLevelElement.value.asDexValueInt().getValue(); + ComputedApiLevel computedApiLevel = getSerializedApiLevel(apiLevelCompute, apiLevelValue); + SyntheticKind syntheticKind = synthetics.getNaming().fromId(kindElement.value.asDexValueInt().getValue()); - return kind; + assert syntheticKind != synthetics.getNaming().API_MODEL_OUTLINE + || computedApiLevel.isKnownApiLevel(); + return SynthesizedAnnotationClassInfo.create(syntheticKind, computedApiLevel); + } + + private static int getApiLevelForSerialization(ComputedApiLevel computedApiLevel) { + if (computedApiLevel.isNotSetApiLevel()) { + return NOT_SET_API_LEVEL; + } else if (computedApiLevel.isUnknownApiLevel()) { + return UNKNOWN_API_LEVEL; + } else { + assert computedApiLevel.isKnownApiLevel(); + return computedApiLevel.asKnownApiLevel().getApiLevel().getLevel(); + } + } + + private static ComputedApiLevel getSerializedApiLevel( + AndroidApiLevelCompute apiLevelCompute, int apiLevelValue) { + if (apiLevelValue == NOT_SET_API_LEVEL) { + return ComputedApiLevel.notSet(); + } else if (apiLevelValue == UNKNOWN_API_LEVEL) { + return ComputedApiLevel.unknown(); + } else { + return apiLevelCompute.of(AndroidApiLevel.getAndroidApiLevel(apiLevelValue)); + } } public DexAnnotation rewrite(Function<DexEncodedAnnotation, DexEncodedAnnotation> rewriter) { @@ -535,4 +582,29 @@ } return new DexAnnotation(visibility, rewritten); } + + public static class SynthesizedAnnotationClassInfo { + + private final SyntheticKind syntheticKind; + private final ComputedApiLevel computedApiLevel; + + private SynthesizedAnnotationClassInfo( + SyntheticKind syntheticKind, ComputedApiLevel computedApiLevel) { + this.syntheticKind = syntheticKind; + this.computedApiLevel = computedApiLevel; + } + + private static SynthesizedAnnotationClassInfo create( + SyntheticKind syntheticKind, ComputedApiLevel computedApiLevel) { + return new SynthesizedAnnotationClassInfo(syntheticKind, computedApiLevel); + } + + public SyntheticKind getSyntheticKind() { + return syntheticKind; + } + + public ComputedApiLevel getComputedApiLevel() { + return computedApiLevel; + } + } }
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java index 4ffba91..df3709f 100644 --- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java +++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -356,6 +356,7 @@ public final DexString valueString = createString("value"); public final DexString kindString = createString("kind"); public final DexString versionHashString = createString("versionHash"); + public final DexString apiLevelString = createString("apiLevel"); // Prefix for runtime affecting yet potential class-retained annotations. public final DexString dalvikAnnotationOptimizationPrefix =
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java index 22d3978..01fa0e0 100644 --- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java +++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -265,6 +265,10 @@ forEachStaticField(field -> consumer.accept(new ProgramField(this, field))); } + public void forEachProgramStaticMethod(Consumer<? super ProgramMethod> consumer) { + forEachProgramDirectMethodMatching(DexEncodedMethod::isStatic, consumer); + } + public void forEachProgramMember(Consumer<? super ProgramMember<?, ?>> consumer) { forEachProgramField(consumer); forEachProgramMethod(consumer); @@ -310,7 +314,7 @@ } public void forEachProgramDirectMethodMatching( - Predicate<DexEncodedMethod> predicate, Consumer<ProgramMethod> consumer) { + Predicate<DexEncodedMethod> predicate, Consumer<? super ProgramMethod> consumer) { methodCollection.forEachDirectMethodMatching( predicate, method -> consumer.accept(new ProgramMethod(this, method))); }
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java index 43558a8..b43256b 100644 --- a/src/main/java/com/android/tools/r8/graph/GraphLens.java +++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -9,6 +9,7 @@ import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription; import com.android.tools.r8.ir.code.Invoke.Type; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; +import com.android.tools.r8.ir.optimize.enums.EnumUnboxingLens; import com.android.tools.r8.optimize.MemberRebindingIdentityLens; import com.android.tools.r8.optimize.MemberRebindingLens; import com.android.tools.r8.shaking.KeepInfoCollection; @@ -600,6 +601,10 @@ return false; } + public EnumUnboxingLens asEnumUnboxerLens() { + return null; + } + public boolean isHorizontalClassMergerGraphLens() { return false; }
diff --git a/src/main/java/com/android/tools/r8/graph/MethodResolution.java b/src/main/java/com/android/tools/r8/graph/MethodResolution.java index 11f9bd9..a56b0ef 100644 --- a/src/main/java/com/android/tools/r8/graph/MethodResolution.java +++ b/src/main/java/com/android/tools/r8/graph/MethodResolution.java
@@ -205,9 +205,12 @@ && !superClass.isLibraryClass()) { return; } - builder.addResolutionResult( + MethodResolutionResult superTypeResult = resolveMethodOnClassStep2( - superClass, methodProto, methodName, initialResolutionHolder)); + superClass, methodProto, methodName, initialResolutionHolder); + if (superTypeResult != null) { + builder.addResolutionResult(superTypeResult); + } }); return builder.buildOrIfEmpty(null, clazz.superType); }
diff --git a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java index 8c949fe..ef0d7ff 100644 --- a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java +++ b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
@@ -1213,6 +1213,10 @@ super(typesCausingError); } + public static NoSuchMethodResult getEmptyNoSuchMethodResult() { + return INSTANCE; + } + @Override public boolean isNoSuchMethodErrorResult(DexClass context, AppInfoWithClassHierarchy appInfo) { return true;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java index b84809e..b8782e2 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -154,7 +154,12 @@ appView.setHorizontallyMergedClasses(mergedClasses, mode); HorizontalClassMergerGraphLens horizontalClassMergerGraphLens = - createLens(mergedClasses, lensBuilder, mode, syntheticArgumentClass); + createLens( + mergedClasses, + lensBuilder, + mode, + artProfileCollectionAdditions, + syntheticArgumentClass); artProfileCollectionAdditions = artProfileCollectionAdditions.rewriteMethodReferences( horizontalClassMergerGraphLens::getNextMethodToInvoke); @@ -185,7 +190,7 @@ .appInfo() .getMainDexInfo() .rewrittenWithLens(syntheticItems, horizontalClassMergerGraphLens))); - appView.setGraphLens(horizontalClassMergerGraphLens); + appView.rewriteWithD8Lens(horizontalClassMergerGraphLens); } codeProvider.setGraphLens(horizontalClassMergerGraphLens); @@ -391,8 +396,15 @@ HorizontallyMergedClasses mergedClasses, HorizontalClassMergerGraphLens.Builder lensBuilder, Mode mode, + ArtProfileCollectionAdditions artProfileCollectionAdditions, SyntheticArgumentClass syntheticArgumentClass) { - return new TreeFixer(appView, mergedClasses, lensBuilder, mode, syntheticArgumentClass) + return new TreeFixer( + appView, + mergedClasses, + lensBuilder, + mode, + artProfileCollectionAdditions, + syntheticArgumentClass) .fixupTypeReferences(); }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java index 51b4939..53f08a6 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -99,7 +99,7 @@ private final MutableBidirectionalManyToOneRepresentativeMap<DexField, DexField> newFieldSignatures = BidirectionalManyToOneRepresentativeHashMap.newIdentityHashMap(); - private final MutableBidirectionalManyToOneMap<DexMethod, DexMethod> methodMap = + final MutableBidirectionalManyToOneMap<DexMethod, DexMethod> methodMap = BidirectionalManyToOneHashMap.newIdentityHashMap(); private final MutableBidirectionalManyToOneRepresentativeMap<DexMethod, DexMethod> newMethodSignatures = BidirectionalManyToOneRepresentativeHashMap.newIdentityHashMap();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java index be7bf1c..1e3e943 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
@@ -12,7 +12,6 @@ import com.android.tools.r8.horizontalclassmerging.policies.AllInstantiatedOrUninstantiated; import com.android.tools.r8.horizontalclassmerging.policies.CheckAbstractClasses; import com.android.tools.r8.horizontalclassmerging.policies.CheckSyntheticClasses; -import com.android.tools.r8.horizontalclassmerging.policies.ComputeApiLevelOfSyntheticClass; import com.android.tools.r8.horizontalclassmerging.policies.FinalizeMergeGroup; import com.android.tools.r8.horizontalclassmerging.policies.LimitClassGroups; import com.android.tools.r8.horizontalclassmerging.policies.LimitInterfaceGroups; @@ -58,7 +57,7 @@ import com.android.tools.r8.horizontalclassmerging.policies.SameInstanceFields; import com.android.tools.r8.horizontalclassmerging.policies.SameMainDexGroup; import com.android.tools.r8.horizontalclassmerging.policies.SameNestHost; -import com.android.tools.r8.horizontalclassmerging.policies.SamePackageForApiOutline; +import com.android.tools.r8.horizontalclassmerging.policies.SamePackageForNonGlobalMergeSynthetic; import com.android.tools.r8.horizontalclassmerging.policies.SameParentClass; import com.android.tools.r8.horizontalclassmerging.policies.SyntheticItemsPolicy; import com.android.tools.r8.horizontalclassmerging.policies.VerifyMultiClassPolicyAlwaysSatisfied; @@ -143,8 +142,7 @@ ImmutableList.Builder<SingleClassPolicy> builder = ImmutableList.<SingleClassPolicy>builder() .add(new CheckSyntheticClasses(appView)) - .add(new OnlyClassesWithStaticDefinitionsAndNoClassInitializer()) - .add(new ComputeApiLevelOfSyntheticClass(appView)); + .add(new OnlyClassesWithStaticDefinitionsAndNoClassInitializer()); assert verifySingleClassPoliciesIrrelevantForMergingSyntheticsInD8(appView, mode, builder); return builder.build(); } @@ -267,7 +265,7 @@ new SameParentClass(), new SyntheticItemsPolicy(appView, mode), new NoApiOutlineWithNonApiOutline(appView), - new SamePackageForApiOutline(appView, mode), + new SamePackageForNonGlobalMergeSynthetic(appView), new NoDifferentApiReferenceLevel(appView), new LimitClassGroups(appView)); assert verifyMultiClassPoliciesIrrelevantForMergingSyntheticsInD8(appView, mode, builder);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java index 31753bf..a4b8ae0 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -21,8 +21,10 @@ import com.android.tools.r8.graph.TreeFixerBase; import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode; import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter; +import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions; import com.android.tools.r8.shaking.AnnotationFixer; import com.android.tools.r8.utils.ArrayUtils; +import com.android.tools.r8.utils.Box; import com.android.tools.r8.utils.OptionalBool; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; @@ -47,6 +49,7 @@ private final Mode mode; private final HorizontalClassMergerGraphLens.Builder lensBuilder; private final DexItemFactory dexItemFactory; + private final ArtProfileCollectionAdditions artProfileCollectionAdditions; private final SyntheticArgumentClass syntheticArgumentClass; private final Map<DexProgramClass, DexType> originalSuperTypes = new IdentityHashMap<>(); @@ -58,12 +61,14 @@ HorizontallyMergedClasses mergedClasses, HorizontalClassMergerGraphLens.Builder lensBuilder, Mode mode, + ArtProfileCollectionAdditions artProfileCollectionAdditions, SyntheticArgumentClass syntheticArgumentClass) { super(appView); this.appView = appView; this.mergedClasses = mergedClasses; this.mode = mode; this.lensBuilder = lensBuilder; + this.artProfileCollectionAdditions = artProfileCollectionAdditions; this.syntheticArgumentClass = syntheticArgumentClass; this.dexItemFactory = appView.dexItemFactory(); } @@ -289,15 +294,36 @@ if (method.isInstanceInitializer()) { // If the method is an instance initializer, then add extra nulls. + Box<Set<DexType>> usedSyntheticArgumentClasses = new Box<>(); newMethodReference = dexItemFactory.createInstanceInitializerWithFreshProto( newMethodReference, syntheticArgumentClass.getArgumentClasses(), - tryMethod -> !newMethods.contains(tryMethod.getSignature())); + tryMethod -> !newMethods.contains(tryMethod.getSignature()), + usedSyntheticArgumentClasses::set); lensBuilder.addExtraParameters( originalMethodReference, ExtraUnusedNullParameter.computeExtraUnusedNullParameters( originalMethodReference, newMethodReference)); + + // Amend the art profile collection. + if (usedSyntheticArgumentClasses.isSet()) { + Set<DexMethod> previousMethodReferences = + lensBuilder.methodMap.getKeys(originalMethodReference); + if (previousMethodReferences.isEmpty()) { + artProfileCollectionAdditions.applyIfContextIsInProfile( + originalMethodReference, + additionsBuilder -> + usedSyntheticArgumentClasses.get().forEach(additionsBuilder::addRule)); + } else { + for (DexMethod previousMethodReference : previousMethodReferences) { + artProfileCollectionAdditions.applyIfContextIsInProfile( + previousMethodReference, + additionsBuilder -> + usedSyntheticArgumentClasses.get().forEach(additionsBuilder::addRule)); + } + } + } } else { newMethodReference = dexItemFactory.createFreshMethodNameWithoutHolder(
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/ComputeApiLevelOfSyntheticClass.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/ComputeApiLevelOfSyntheticClass.java deleted file mode 100644 index 9038a57..0000000 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/ComputeApiLevelOfSyntheticClass.java +++ /dev/null
@@ -1,205 +0,0 @@ -// Copyright (c) 2022, 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.horizontalclassmerging.policies; - -import com.android.tools.r8.androidapi.AndroidApiLevelCompute; -import com.android.tools.r8.androidapi.ComputedApiLevel; -import com.android.tools.r8.dex.code.CfOrDexInstruction; -import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.DexCallSite; -import com.android.tools.r8.graph.DexEncodedMethod; -import com.android.tools.r8.graph.DexField; -import com.android.tools.r8.graph.DexMethod; -import com.android.tools.r8.graph.DexMethodHandle; -import com.android.tools.r8.graph.DexProgramClass; -import com.android.tools.r8.graph.DexReference; -import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.ProgramMethod; -import com.android.tools.r8.graph.UseRegistry; -import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy; -import com.android.tools.r8.synthesis.SyntheticItems; -import java.util.ListIterator; - -public class ComputeApiLevelOfSyntheticClass extends SingleClassPolicy { - - private final AppView<?> appView; - private final SyntheticItems syntheticItems; - - public ComputeApiLevelOfSyntheticClass(AppView<?> appView) { - this.appView = appView; - this.syntheticItems = appView.getSyntheticItems(); - } - - @Override - public boolean canMerge(DexProgramClass clazz) { - assert syntheticItems.isSyntheticClass(clazz); - clazz.forEachProgramMethod( - programMethod -> { - DexEncodedMethod definition = programMethod.getDefinition(); - if (definition.getApiLevelForCode().isNotSetApiLevel()) { - ComputeApiLevelUseRegistry computeApiLevel = - new ComputeApiLevelUseRegistry(appView, programMethod, appView.apiLevelCompute()); - computeApiLevel.accept(programMethod); - ComputedApiLevel maxApiReferenceLevel = computeApiLevel.getMaxApiReferenceLevel(); - assert !maxApiReferenceLevel.isNotSetApiLevel(); - definition.setApiLevelForCode(maxApiReferenceLevel); - definition.setApiLevelForDefinition(computeApiLevel.getMaxApiReferenceLevel()); - } - }); - return true; - } - - @Override - public String getName() { - return "ComputeApiLevelOfSyntheticClass"; - } - - private static class ComputeApiLevelUseRegistry extends UseRegistry<ProgramMethod> { - - private final AppView<?> appView; - private final AndroidApiLevelCompute apiLevelCompute; - private ComputedApiLevel maxApiReferenceLevel; - - public ComputeApiLevelUseRegistry( - AppView<?> appView, ProgramMethod context, AndroidApiLevelCompute apiLevelCompute) { - super(appView, context); - this.appView = appView; - this.apiLevelCompute = apiLevelCompute; - maxApiReferenceLevel = appView.computedMinApiLevel(); - } - - @Override - public void registerInitClass(DexType clazz) { - assert false : "Unexpected call to an instruction that should not exist on DEX"; - } - - @Override - public void registerRecordFieldValues(DexField[] fields) { - assert false : "Unexpected call to an instruction that should not exist on DEX"; - } - - @Override - public void registerInvokeVirtual(DexMethod invokedMethod) { - setMaxApiReferenceLevel(invokedMethod); - } - - @Override - public void registerInvokeDirect(DexMethod invokedMethod) { - setMaxApiReferenceLevel(invokedMethod); - } - - @Override - public void registerInvokeStatic(DexMethod invokedMethod) { - setMaxApiReferenceLevel(invokedMethod); - } - - @Override - public void registerInvokeInterface(DexMethod invokedMethod) { - setMaxApiReferenceLevel(invokedMethod); - } - - @Override - public void registerInvokeSuper(DexMethod invokedMethod) { - setMaxApiReferenceLevel(invokedMethod); - } - - @Override - public void registerInstanceFieldRead(DexField field) { - setMaxApiReferenceLevel(field); - } - - @Override - public void registerInstanceFieldReadFromMethodHandle(DexField field) { - setMaxApiReferenceLevel(field); - } - - @Override - public void registerInstanceFieldWrite(DexField field) { - setMaxApiReferenceLevel(field); - } - - @Override - public void registerInstanceFieldWriteFromMethodHandle(DexField field) { - setMaxApiReferenceLevel(field); - } - - @Override - public void registerNewInstance(DexType type) { - setMaxApiReferenceLevel(type); - } - - @Override - public void registerStaticFieldRead(DexField field) { - setMaxApiReferenceLevel(field); - } - - @Override - public void registerStaticFieldReadFromMethodHandle(DexField field) { - setMaxApiReferenceLevel(field); - } - - @Override - public void registerStaticFieldWrite(DexField field) { - setMaxApiReferenceLevel(field); - } - - @Override - public void registerStaticFieldWriteFromMethodHandle(DexField field) { - setMaxApiReferenceLevel(field); - } - - @Override - public void registerConstClass( - DexType type, - ListIterator<? extends CfOrDexInstruction> iterator, - boolean ignoreCompatRules) { - // Intentionally empty. - } - - @Override - public void registerCheckCast(DexType type, boolean ignoreCompatRules) { - // Intentionally empty. - } - - @Override - public void registerSafeCheckCast(DexType type) { - // Intentionally empty. - } - - @Override - public void registerTypeReference(DexType type) { - // Intentionally empty. - } - - @Override - public void registerInstanceOf(DexType type) { - // Intentionally empty. - } - - @Override - public void registerExceptionGuard(DexType guard) { - setMaxApiReferenceLevel(guard); - } - - @Override - public void registerMethodHandle(DexMethodHandle methodHandle, MethodHandleUse use) { - assert false : "Unexpected call to an instruction that should not exist on DEX"; - } - - @Override - public void registerCallSite(DexCallSite callSite) { - assert false : "Unexpected call to an instruction that should not exist on DEX"; - } - - private void setMaxApiReferenceLevel(DexReference reference) { - maxApiReferenceLevel = - maxApiReferenceLevel.max(apiLevelCompute.computeApiLevelForLibraryReference(reference)); - } - - public ComputedApiLevel getMaxApiReferenceLevel() { - return maxApiReferenceLevel; - } - } -}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java index a021a28..0e899f7 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java
@@ -5,6 +5,7 @@ package com.android.tools.r8.horizontalclassmerging.policies; import static com.android.tools.r8.utils.AndroidApiLevelUtils.getApiReferenceLevelForMerging; +import static com.android.tools.r8.utils.AndroidApiLevelUtils.getMembersApiReferenceLevelForMerging; import com.android.tools.r8.androidapi.AndroidApiLevelCompute; import com.android.tools.r8.androidapi.ComputedApiLevel; @@ -15,14 +16,16 @@ public class NoDifferentApiReferenceLevel extends MultiClassSameReferencePolicy<ComputedApiLevel> { private final AndroidApiLevelCompute apiLevelCompute; - private final AppView<?> appView; private final boolean enableApiCallerIdentification; + private final boolean enableWholeProgramOptimization; + private final ComputedApiLevel minApiLevel; public NoDifferentApiReferenceLevel(AppView<?> appView) { - this.appView = appView; apiLevelCompute = appView.apiLevelCompute(); enableApiCallerIdentification = appView.options().apiModelingOptions().isApiCallerIdentificationEnabled(); + enableWholeProgramOptimization = appView.enableWholeProgramOptimizations(); + minApiLevel = appView.computedMinApiLevel(); } @Override @@ -38,6 +41,13 @@ @Override public ComputedApiLevel getMergeKey(DexProgramClass clazz) { assert enableApiCallerIdentification; - return getApiReferenceLevelForMerging(appView, apiLevelCompute, clazz); + ComputedApiLevel apiReferenceLevelForMerging = + enableWholeProgramOptimization + ? getApiReferenceLevelForMerging(apiLevelCompute, clazz) + : getMembersApiReferenceLevelForMerging(clazz, minApiLevel); + if (apiReferenceLevelForMerging.isUnknownApiLevel()) { + return ineligibleForClassMerging(); + } + return apiReferenceLevelForMerging; } }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SamePackageForApiOutline.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SamePackageForNonGlobalMergeSynthetic.java similarity index 83% rename from src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SamePackageForApiOutline.java rename to src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SamePackageForNonGlobalMergeSynthetic.java index 30a3200..db6d282 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SamePackageForApiOutline.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SamePackageForNonGlobalMergeSynthetic.java
@@ -9,23 +9,21 @@ import com.android.tools.r8.graph.AppInfo; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexProgramClass; -import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode; import com.android.tools.r8.horizontalclassmerging.MergeGroup; import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy; import com.android.tools.r8.synthesis.SyntheticItems; +import com.google.common.collect.Iterables; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; -public class SamePackageForApiOutline extends MultiClassPolicy { +public class SamePackageForNonGlobalMergeSynthetic extends MultiClassPolicy { private final AppView<AppInfo> appView; - private final Mode mode; - public SamePackageForApiOutline(AppView<AppInfo> appView, Mode mode) { + public SamePackageForNonGlobalMergeSynthetic(AppView<AppInfo> appView) { this.appView = appView; - this.mode = mode; } /** Sort unrestricted classes into restricted classes if they are in the same package. */ @@ -50,7 +48,12 @@ // Sort all restricted classes into packages. for (DexProgramClass clazz : group) { - if (syntheticItems.isSyntheticOfKind(clazz.getType(), k -> k.API_MODEL_OUTLINE)) { + assert syntheticItems.isSynthetic(clazz.getType()); + if (Iterables.any( + syntheticItems.getSyntheticKinds(clazz.getType()), + kind -> + !kind.isSyntheticMethodKind() + || !kind.asSyntheticMethodKind().isAllowGlobalMerging())) { restrictedClasses .computeIfAbsent( clazz.getType().getPackageDescriptor(), ignoreArgument(MergeGroup::new))
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/VerifySingleClassPolicyAlwaysSatisfied.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/VerifySingleClassPolicyAlwaysSatisfied.java index 51d33b7..47824f9 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/VerifySingleClassPolicyAlwaysSatisfied.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/VerifySingleClassPolicyAlwaysSatisfied.java
@@ -18,7 +18,7 @@ @Override public boolean canMerge(DexProgramClass program) { - assert policy.canMerge(program); + assert policy.canMerge(program) : "Verification of single class policies failed"; return true; }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Argument.java b/src/main/java/com/android/tools/r8/ir/code/Argument.java index 182b9b4..587ef11 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Argument.java +++ b/src/main/java/com/android/tools/r8/ir/code/Argument.java
@@ -16,7 +16,7 @@ import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult; import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; -import com.android.tools.r8.lightir.LIRBuilder; +import com.android.tools.r8.lightir.LirBuilder; import java.util.Set; /** @@ -177,7 +177,7 @@ } @Override - public void buildLIR(LIRBuilder<Value, BasicBlock> builder) { + public void buildLir(LirBuilder<Value, BasicBlock> builder) { builder.addArgument(index, knownToBeBoolean); } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java index 5715e54..b3c3bbc 100644 --- a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java +++ b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
@@ -17,7 +17,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.ir.regalloc.RegisterAllocator; -import com.android.tools.r8.lightir.LIRBuilder; +import com.android.tools.r8.lightir.LirBuilder; public class ArrayLength extends Instruction { @@ -154,7 +154,7 @@ } @Override - public void buildLIR(LIRBuilder<Value, BasicBlock> builder) { + public void buildLir(LirBuilder<Value, BasicBlock> builder) { builder.addArrayLength(array()); } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java index c462cf6..04857de 100644 --- a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java +++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -28,7 +28,7 @@ import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.conversion.CfBuilder; import com.android.tools.r8.ir.conversion.DexBuilder; -import com.android.tools.r8.lightir.LIRBuilder; +import com.android.tools.r8.lightir.LirBuilder; import com.android.tools.r8.utils.InternalOutputMode; import com.android.tools.r8.utils.NumberUtils; import java.util.Set; @@ -346,7 +346,7 @@ } @Override - public void buildLIR(LIRBuilder<Value, BasicBlock> builder) { + public void buildLir(LirBuilder<Value, BasicBlock> builder) { builder.addConstNumber(outType(), value); } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstString.java b/src/main/java/com/android/tools/r8/ir/code/ConstString.java index 900fe91..7201b71 100644 --- a/src/main/java/com/android/tools/r8/ir/code/ConstString.java +++ b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
@@ -22,7 +22,7 @@ import com.android.tools.r8.ir.conversion.CfBuilder; import com.android.tools.r8.ir.conversion.DexBuilder; import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult; -import com.android.tools.r8.lightir.LIRBuilder; +import com.android.tools.r8.lightir.LirBuilder; import java.io.UTFDataFormatException; public class ConstString extends ConstInstruction { @@ -182,7 +182,7 @@ } @Override - public void buildLIR(LIRBuilder<Value, BasicBlock> builder) { + public void buildLir(LirBuilder<Value, BasicBlock> builder) { builder.addConstString(value); } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java index 834fd24..8c0eba5 100644 --- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java +++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
@@ -10,7 +10,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.ir.analysis.VerifyTypesHelper; import com.android.tools.r8.ir.conversion.CfBuilder; -import com.android.tools.r8.lightir.LIRBuilder; +import com.android.tools.r8.lightir.LirBuilder; /** * Instruction introducing an SSA value with attached local information. @@ -90,7 +90,7 @@ } @Override - public void buildLIR(LIRBuilder<Value, BasicBlock> builder) { + public void buildLir(LirBuilder<Value, BasicBlock> builder) { builder.addDebugLocalWrite(src()); } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java index 651fb35..361dd22 100644 --- a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java +++ b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
@@ -13,7 +13,7 @@ import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult; import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; -import com.android.tools.r8.lightir.LIRBuilder; +import com.android.tools.r8.lightir.LirBuilder; public class DebugPosition extends Instruction { @@ -101,7 +101,7 @@ } @Override - public void buildLIR(LIRBuilder<Value, BasicBlock> builder) { + public void buildLir(LirBuilder<Value, BasicBlock> builder) { builder.addDebugPosition(getPosition()); } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Div.java b/src/main/java/com/android/tools/r8/ir/code/Div.java index 4f99960..eac1fbf 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Div.java +++ b/src/main/java/com/android/tools/r8/ir/code/Div.java
@@ -17,7 +17,7 @@ import com.android.tools.r8.dex.code.DexInstruction; import com.android.tools.r8.ir.analysis.constant.Bottom; import com.android.tools.r8.ir.analysis.constant.LatticeElement; -import com.android.tools.r8.lightir.LIRBuilder; +import com.android.tools.r8.lightir.LirBuilder; import java.util.function.Function; public class Div extends ArithmeticBinop { @@ -151,7 +151,7 @@ } @Override - public void buildLIR(LIRBuilder<Value, BasicBlock> builder) { + public void buildLir(LirBuilder<Value, BasicBlock> builder) { builder.addDiv(type, leftValue(), rightValue()); } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Goto.java b/src/main/java/com/android/tools/r8/ir/code/Goto.java index b953a68..886ca9a 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Goto.java +++ b/src/main/java/com/android/tools/r8/ir/code/Goto.java
@@ -7,7 +7,7 @@ import com.android.tools.r8.cf.code.CfGoto; import com.android.tools.r8.ir.conversion.CfBuilder; import com.android.tools.r8.ir.conversion.DexBuilder; -import com.android.tools.r8.lightir.LIRBuilder; +import com.android.tools.r8.lightir.LirBuilder; import com.android.tools.r8.utils.CfgPrinter; import java.util.List; import java.util.ListIterator; @@ -128,7 +128,7 @@ } @Override - public void buildLIR(LIRBuilder<Value, BasicBlock> builder) { + public void buildLir(LirBuilder<Value, BasicBlock> builder) { builder.addGoto(getTarget()); }
diff --git a/src/main/java/com/android/tools/r8/ir/code/If.java b/src/main/java/com/android/tools/r8/ir/code/If.java index 12089ec..1cd76a4 100644 --- a/src/main/java/com/android/tools/r8/ir/code/If.java +++ b/src/main/java/com/android/tools/r8/ir/code/If.java
@@ -13,7 +13,7 @@ import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.conversion.CfBuilder; import com.android.tools.r8.ir.conversion.DexBuilder; -import com.android.tools.r8.lightir.LIRBuilder; +import com.android.tools.r8.lightir.LirBuilder; import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.CfgPrinter; import com.android.tools.r8.utils.InternalOutputMode; @@ -292,7 +292,7 @@ } @Override - public void buildLIR(LIRBuilder<Value, BasicBlock> builder) { + public void buildLir(LirBuilder<Value, BasicBlock> builder) { ValueType ifType = inValues.get(0).outType(); if (inValues.size() == 1) { builder.addIf(type, ifType, inValues.get(0), getTrueTarget());
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java index 8892da0..8d7088f 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java +++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -35,7 +35,7 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.ir.regalloc.RegisterAllocator; -import com.android.tools.r8.lightir.LIRBuilder; +import com.android.tools.r8.lightir.LirBuilder; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.CfgPrinter; import com.android.tools.r8.utils.InternalOptions; @@ -1563,7 +1563,7 @@ return false; } - public void buildLIR(LIRBuilder<Value, BasicBlock> builder) { + public void buildLir(LirBuilder<Value, BasicBlock> builder) { throw new Unimplemented("Missing impl for " + getClass().getSimpleName()); }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java index 9203e93..14fdd11 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -29,7 +29,7 @@ import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult; import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; -import com.android.tools.r8.lightir.LIRBuilder; +import com.android.tools.r8.lightir.LirBuilder; import com.android.tools.r8.shaking.AppInfoWithLiveness; import java.util.List; @@ -218,7 +218,7 @@ } @Override - public void buildLIR(LIRBuilder<Value, BasicBlock> builder) { + public void buildLir(LirBuilder<Value, BasicBlock> builder) { builder.addInvokeDirect(getInvokedMethod(), arguments()); }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java index 5809552..3cee2ff 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -27,7 +27,7 @@ import com.android.tools.r8.ir.conversion.DexBuilder; import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; -import com.android.tools.r8.lightir.LIRBuilder; +import com.android.tools.r8.lightir.LirBuilder; import com.android.tools.r8.shaking.AppInfoWithLiveness; import java.util.List; @@ -204,7 +204,7 @@ } @Override - public void buildLIR(LIRBuilder<Value, BasicBlock> builder) { + public void buildLir(LirBuilder<Value, BasicBlock> builder) { builder.addInvokeVirtual(getInvokedMethod(), arguments()); } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/MoveException.java b/src/main/java/com/android/tools/r8/ir/code/MoveException.java index acd2cc4..9cf1d30 100644 --- a/src/main/java/com/android/tools/r8/ir/code/MoveException.java +++ b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
@@ -17,7 +17,7 @@ import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult; import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; -import com.android.tools.r8.lightir.LIRBuilder; +import com.android.tools.r8.lightir.LirBuilder; import com.android.tools.r8.utils.InternalOptions; public class MoveException extends Instruction { @@ -134,7 +134,7 @@ } @Override - public void buildLIR(LIRBuilder<Value, BasicBlock> builder) { + public void buildLir(LirBuilder<Value, BasicBlock> builder) { builder.addMoveException(exceptionType); } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Return.java b/src/main/java/com/android/tools/r8/ir/code/Return.java index d8882e4..1eb399b 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Return.java +++ b/src/main/java/com/android/tools/r8/ir/code/Return.java
@@ -19,7 +19,7 @@ import com.android.tools.r8.ir.conversion.DexBuilder; import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; -import com.android.tools.r8.lightir.LIRBuilder; +import com.android.tools.r8.lightir.LirBuilder; public class Return extends JumpInstruction { @@ -153,7 +153,7 @@ } @Override - public void buildLIR(LIRBuilder<Value, BasicBlock> builder) { + public void buildLir(LirBuilder<Value, BasicBlock> builder) { if (hasReturnValue()) { builder.addReturn(returnValue()); } else {
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java index afad1ff..4d767b0 100644 --- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java +++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -32,7 +32,7 @@ import com.android.tools.r8.ir.conversion.DexBuilder; import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; -import com.android.tools.r8.lightir.LIRBuilder; +import com.android.tools.r8.lightir.LirBuilder; import com.android.tools.r8.shaking.AppInfoWithLiveness; import java.util.Set; @@ -300,7 +300,7 @@ } @Override - public void buildLIR(LIRBuilder<Value, BasicBlock> builder) { + public void buildLir(LirBuilder<Value, BasicBlock> builder) { builder.addStaticGet(getField()); } }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java index 7b3641f..7cbb122 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
@@ -117,7 +117,8 @@ ArtProfileCollectionAdditions artProfileCollectionAdditions = methodProcessor.getArtProfileCollectionAdditions(); CfClassSynthesizerDesugaringEventConsumer classSynthesizerEventConsumer = - CfClassSynthesizerDesugaringEventConsumer.create(artProfileCollectionAdditions); + CfClassSynthesizerDesugaringEventConsumer.createForD8( + appView, artProfileCollectionAdditions); converter.classSynthesisDesugaring(executorService, classSynthesizerEventConsumer); if (!classSynthesizerEventConsumer.getSynthesizedClasses().isEmpty()) { classes =
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java index 7a04167..9a81a28 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
@@ -11,7 +11,6 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.callgraph.CallSiteInformation; import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer; -import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer.D8CfInstructionDesugaringEventConsumer; import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore; import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions; import com.android.tools.r8.utils.ThreadUtils; @@ -50,7 +49,7 @@ ExecutorService executorService) { this.artProfileCollectionAdditions = artProfileCollectionAdditions; this.converter = converter; - this.eventConsumer = MethodProcessorEventConsumer.create(artProfileCollectionAdditions); + this.eventConsumer = MethodProcessorEventConsumer.createForD8(artProfileCollectionAdditions); this.executorService = executorService; this.processorContext = converter.appView.createProcessorContext(); } @@ -90,7 +89,7 @@ } public void scheduleMethodForProcessing( - ProgramMethod method, D8CfInstructionDesugaringEventConsumer eventConsumer) { + ProgramMethod method, CfInstructionDesugaringEventConsumer eventConsumer) { // TODO(b/179755192): By building up waves of methods in the class converter, we can avoid the // following check and always process the method asynchronously. if (!scheduled.contains(method.getHolderType())
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java index 6d6edbf..4eeb553 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -60,9 +60,9 @@ import com.android.tools.r8.ir.optimize.outliner.Outliner; import com.android.tools.r8.ir.optimize.string.StringBuilderAppendOptimizer; import com.android.tools.r8.ir.optimize.string.StringOptimizer; -import com.android.tools.r8.lightir.IR2LIRConverter; -import com.android.tools.r8.lightir.LIR2IRConverter; -import com.android.tools.r8.lightir.LIRCode; +import com.android.tools.r8.lightir.IR2LirConverter; +import com.android.tools.r8.lightir.Lir2IRConverter; +import com.android.tools.r8.lightir.LirCode; import com.android.tools.r8.logging.Log; import com.android.tools.r8.naming.IdentifierNameStringMarker; import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorIROptimizer; @@ -220,7 +220,7 @@ : CfInstructionDesugaringCollection.create(appView, appView.apiLevelCompute()); this.covariantReturnTypeAnnotationTransformer = options.processCovariantReturnTypeAnnotations - ? new CovariantReturnTypeAnnotationTransformer(this, appView.dexItemFactory()) + ? new CovariantReturnTypeAnnotationTransformer(appView, this) : null; if (appView.options().desugarState.isOn() && appView.options().apiModelingOptions().enableOutliningOfMethods) { @@ -633,7 +633,7 @@ if (serviceLoaderRewriter != null) { assert appView.appInfo().hasLiveness(); timing.begin("Rewrite service loaders"); - serviceLoaderRewriter.rewrite(code, methodProcessingContext); + serviceLoaderRewriter.rewrite(code, methodProcessor, methodProcessingContext); timing.end(); } @@ -765,7 +765,8 @@ timing.end(); if (assertionErrorTwoArgsConstructorRewriter != null) { timing.begin("Rewrite AssertionError"); - assertionErrorTwoArgsConstructorRewriter.rewrite(code, methodProcessingContext); + assertionErrorTwoArgsConstructorRewriter.rewrite( + code, methodProcessor, methodProcessingContext); timing.end(); } timing.begin("Run CSE"); @@ -1078,8 +1079,8 @@ OptimizationFeedback feedback, BytecodeMetadataProvider bytecodeMetadataProvider, Timing timing) { - if (options.testing.roundtripThroughLIR) { - code = roundtripThroughLIR(code, feedback, bytecodeMetadataProvider, timing); + if (options.testing.roundtripThroughLir) { + code = roundtripThroughLir(code, feedback, bytecodeMetadataProvider, timing); } if (options.isGeneratingClassFiles()) { timing.begin("IR->CF"); @@ -1094,16 +1095,16 @@ printMethod(code.context(), "After finalization"); } - private IRCode roundtripThroughLIR( + private IRCode roundtripThroughLir( IRCode code, OptimizationFeedback feedback, BytecodeMetadataProvider bytecodeMetadataProvider, Timing timing) { timing.begin("IR->LIR"); - LIRCode lirCode = IR2LIRConverter.translate(code, appView.dexItemFactory()); + LirCode lirCode = IR2LirConverter.translate(code, appView.dexItemFactory()); timing.end(); timing.begin("LIR->IR"); - IRCode irCode = LIR2IRConverter.translate(code.context(), lirCode, appView); + IRCode irCode = Lir2IRConverter.translate(code.context(), lirCode, appView); timing.end(); return irCode; }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorEventConsumer.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorEventConsumer.java index d002cd5..e360a95 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorEventConsumer.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorEventConsumer.java
@@ -4,24 +4,38 @@ package com.android.tools.r8.ir.conversion; +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; +import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.ir.optimize.AssertionErrorTwoArgsConstructorRewriterEventConsumer; +import com.android.tools.r8.ir.optimize.ServiceLoaderRewriterEventConsumer; import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizationsEventConsumer; import com.android.tools.r8.ir.optimize.api.InstanceInitializerOutlinerEventConsumer; import com.android.tools.r8.ir.optimize.enums.EnumUnboxerMethodProcessorEventConsumer; import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions; import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingMethodProcessorEventConsumer; +import com.android.tools.r8.shaking.AppInfoWithLiveness; public abstract class MethodProcessorEventConsumer - implements EnumUnboxerMethodProcessorEventConsumer, + implements AssertionErrorTwoArgsConstructorRewriterEventConsumer, + EnumUnboxerMethodProcessorEventConsumer, InstanceInitializerOutlinerEventConsumer, + ServiceLoaderRewriterEventConsumer, UtilityMethodsForCodeOptimizationsEventConsumer { - public static MethodProcessorEventConsumer create( + public void finished(AppView<AppInfoWithLiveness> appView) {} + + public static MethodProcessorEventConsumer createForD8( ArtProfileCollectionAdditions artProfileCollectionAdditions) { return ArtProfileRewritingMethodProcessorEventConsumer.attach( artProfileCollectionAdditions, empty()); } + public static MethodProcessorEventConsumer createForR8( + AppView<? extends AppInfoWithClassHierarchy> appView) { + return ArtProfileRewritingMethodProcessorEventConsumer.attach(appView, empty()); + } + public static MethodProcessorEventConsumer empty() { return EmptyMethodProcessorEventConsumer.getInstance(); } @@ -38,6 +52,11 @@ } @Override + public void acceptAssertionErrorCreateMethod(ProgramMethod method, ProgramMethod context) { + // Intentionally empty. + } + + @Override public void acceptEnumUnboxerCheckNotZeroContext(ProgramMethod method, ProgramMethod context) { // Intentionally empty. } @@ -60,6 +79,11 @@ } @Override + public void acceptServiceLoaderLoadUtilityMethod(ProgramMethod method, ProgramMethod context) { + // Intentionally empty. + } + + @Override public void acceptUtilityToStringIfNotNullMethod(ProgramMethod method, ProgramMethod context) { // Intentionally empty. }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java index 1d06a25..9d03bb5 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
@@ -22,6 +22,7 @@ import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer; import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringCollection; import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer; +import com.android.tools.r8.ir.desugar.CovariantReturnTypeAnnotationTransformerEventConsumer; import com.android.tools.r8.ir.desugar.ProgramAdditions; import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter; import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceApplicationRewriter; @@ -91,7 +92,7 @@ new L8InnerOuterAttributeEraser(appView).run(); } - processCovariantReturnTypeAnnotations(builder); + processCovariantReturnTypeAnnotations(builder, artProfileCollectionAdditions); timing.end(); @@ -308,6 +309,7 @@ throws ExecutionException { CfPostProcessingDesugaringEventConsumer eventConsumer = CfPostProcessingDesugaringEventConsumer.createForD8( + appView, methodProcessor.getArtProfileCollectionAdditions(), methodProcessor, instructionDesugaring); @@ -338,9 +340,13 @@ programAdditions.apply(executorService); } - private void processCovariantReturnTypeAnnotations(Builder<?> builder) { + private void processCovariantReturnTypeAnnotations( + Builder<?> builder, ArtProfileCollectionAdditions artProfileCollectionAdditions) { if (covariantReturnTypeAnnotationTransformer != null) { - covariantReturnTypeAnnotationTransformer.process(builder); + covariantReturnTypeAnnotationTransformer.process( + builder, + CovariantReturnTypeAnnotationTransformerEventConsumer.create( + artProfileCollectionAdditions)); } }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java index aae02ab..3b7fcef 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
@@ -16,7 +16,6 @@ import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed; import com.android.tools.r8.logging.Log; import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator; -import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.CfgPrinter; import com.android.tools.r8.utils.Timing; @@ -82,10 +81,8 @@ new PostMethodProcessor.Builder(graphLensForPrimaryOptimizationPass); { timing.begin("Build primary method processor"); - ArtProfileCollectionAdditions artProfileCollectionAdditions = - ArtProfileCollectionAdditions.create(appView); MethodProcessorEventConsumer eventConsumer = - MethodProcessorEventConsumer.create(artProfileCollectionAdditions); + MethodProcessorEventConsumer.createForR8(appView); PrimaryMethodProcessor primaryMethodProcessor = PrimaryMethodProcessor.create( appView.withLiveness(), eventConsumer, executorService, timing); @@ -101,7 +98,7 @@ timing, executorService); lastWaveDone(postMethodProcessorBuilder, executorService); - timing.time("Commit profile additions", () -> artProfileCollectionAdditions.commit(appView)); + eventConsumer.finished(appView); assert appView.graphLens() == graphLensForPrimaryOptimizationPass; timing.end(); } @@ -155,17 +152,14 @@ { timing.begin("IR conversion phase 2"); - ArtProfileCollectionAdditions artProfileCollectionAdditions = - ArtProfileCollectionAdditions.create(appView); + MethodProcessorEventConsumer eventConsumer = + MethodProcessorEventConsumer.createForR8(appView); PostMethodProcessor postMethodProcessor = timing.time( "Build post method processor", - () -> { - MethodProcessorEventConsumer eventConsumer = - MethodProcessorEventConsumer.create(artProfileCollectionAdditions); - return postMethodProcessorBuilder.build( - appView, eventConsumer, executorService, timing); - }); + () -> + postMethodProcessorBuilder.build( + appView, eventConsumer, executorService, timing)); if (postMethodProcessor != null) { assert !options.debug; assert appView.graphLens() == graphLensForSecondaryOptimizationPass; @@ -179,8 +173,7 @@ timing); timing.end(); timing.time("Update visible optimization info", feedback::updateVisibleOptimizationInfo); - timing.time( - "Commit profile additions", () -> artProfileCollectionAdditions.commit(appView)); + eventConsumer.finished(appView); assert appView.graphLens() == graphLensForSecondaryOptimizationPass; } timing.end();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java index f2cdd81..49a7dd8 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java
@@ -4,7 +4,10 @@ package com.android.tools.r8.ir.desugar; +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; +import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.ProgramDefinition; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer; import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterL8SynthesizerEventConsumer; @@ -25,14 +28,24 @@ protected CfClassSynthesizerDesugaringEventConsumer() {} - public static CfClassSynthesizerDesugaringEventConsumer create( - ArtProfileCollectionAdditions artProfileCollectionAdditions) { + public static CfClassSynthesizerDesugaringEventConsumer createForD8( + AppView<?> appView, ArtProfileCollectionAdditions artProfileCollectionAdditions) { CfClassSynthesizerDesugaringEventConsumer eventConsumer = new D8R8CfClassSynthesizerDesugaringEventConsumer(); return ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer.attach( - artProfileCollectionAdditions, eventConsumer); + appView, eventConsumer, artProfileCollectionAdditions); } + public static CfClassSynthesizerDesugaringEventConsumer createForR8( + AppView<? extends AppInfoWithClassHierarchy> appView) { + CfClassSynthesizerDesugaringEventConsumer eventConsumer = + new D8R8CfClassSynthesizerDesugaringEventConsumer(); + return ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer.attach( + appView, eventConsumer); + } + + public void finished(AppView<? extends AppInfoWithClassHierarchy> appView) {} + public abstract Set<DexProgramClass> getSynthesizedClasses(); private static class D8R8CfClassSynthesizerDesugaringEventConsumer @@ -77,12 +90,18 @@ } @Override + public void acceptVarHandleDesugaringClassContext( + DexProgramClass clazz, ProgramDefinition context) { + // Intentionally empty. + } + + @Override public Set<DexProgramClass> getSynthesizedClasses() { return synthesizedClasses; } @Override - public void acceptCollectionConversion(ProgramMethod arrayConversion) { + public void acceptCollectionConversion(ProgramMethod arrayConversion, ProgramMethod context) { synthesizedClasses.add(arrayConversion.getHolder()); } }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java index 595d1db..7290623 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -8,8 +8,10 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexClasspathClass; +import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexReference; +import com.android.tools.r8.graph.ProgramDefinition; import com.android.tools.r8.graph.ProgramField; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.ClassConverterResult; @@ -71,11 +73,14 @@ ArtProfileCollectionAdditions artProfileCollectionAdditions, ClassConverterResult.Builder classConverterResultBuilder, D8MethodProcessor methodProcessor) { - CfInstructionDesugaringEventConsumer eventConsumer = + D8CfInstructionDesugaringEventConsumer eventConsumer = new D8CfInstructionDesugaringEventConsumer( appView, classConverterResultBuilder, methodProcessor); - return ArtProfileRewritingCfInstructionDesugaringEventConsumer.attach( - appView, artProfileCollectionAdditions, eventConsumer); + CfInstructionDesugaringEventConsumer outermostEventConsumer = + ArtProfileRewritingCfInstructionDesugaringEventConsumer.attach( + appView, artProfileCollectionAdditions, eventConsumer); + eventConsumer.setOutermostEventConsumer(outermostEventConsumer); + return outermostEventConsumer; } public static CfInstructionDesugaringEventConsumer createForR8( @@ -114,6 +119,8 @@ private final List<LambdaClass> synthesizedLambdaClasses = new ArrayList<>(); private final List<ConstantDynamicClass> synthesizedConstantDynamicClasses = new ArrayList<>(); + private CfInstructionDesugaringEventConsumer outermostEventConsumer = this; + private D8CfInstructionDesugaringEventConsumer( AppView<?> appView, ClassConverterResult.Builder classConverterResultBuilder, @@ -123,6 +130,11 @@ this.methodProcessor = methodProcessor; } + public void setOutermostEventConsumer( + CfInstructionDesugaringEventConsumer outermostEventConsumer) { + this.outermostEventConsumer = outermostEventConsumer; + } + @Override public void acceptDefaultAsCompanionMethod( ProgramMethod method, ProgramMethod companionMethod) { @@ -151,25 +163,27 @@ } @Override - public void acceptCollectionConversion(ProgramMethod arrayConversion) { - methodProcessor.scheduleMethodForProcessing(arrayConversion, this); + public void acceptCollectionConversion(ProgramMethod arrayConversion, ProgramMethod context) { + methodProcessor.scheduleMethodForProcessing(arrayConversion, outermostEventConsumer); } @Override - public void acceptCovariantRetargetMethod(ProgramMethod method) { - methodProcessor.scheduleMethodForProcessing(method, this); + public void acceptCovariantRetargetMethod(ProgramMethod method, ProgramMethod context) { + methodProcessor.scheduleMethodForProcessing(method, outermostEventConsumer); } @Override public void acceptBackportedMethod(ProgramMethod backportedMethod, ProgramMethod context) { - methodProcessor.scheduleMethodForProcessing(backportedMethod, this); + methodProcessor.scheduleMethodForProcessing(backportedMethod, outermostEventConsumer); } @Override public void acceptBackportedClass(DexProgramClass backportedClass, ProgramMethod context) { backportedClass .programMethods() - .forEach(method -> methodProcessor.scheduleMethodForProcessing(method, this)); + .forEach( + method -> + methodProcessor.scheduleMethodForProcessing(method, outermostEventConsumer)); } @Override @@ -215,7 +229,15 @@ public void acceptVarHandleDesugaringClass(DexProgramClass clazz) { clazz .programMethods() - .forEach(method -> methodProcessor.scheduleMethodForProcessing(method, this)); + .forEach( + method -> + methodProcessor.scheduleMethodForProcessing(method, outermostEventConsumer)); + } + + @Override + public void acceptVarHandleDesugaringClassContext( + DexProgramClass clazz, ProgramDefinition context) { + // Intentionally empty. } @Override @@ -234,6 +256,12 @@ } @Override + public void acceptConstantDynamicRewrittenBootstrapMethod( + ProgramMethod bootstrapMethod, DexMethod oldSignature) { + // Intentionally empty. + } + + @Override public void acceptNestConstructorBridge( ProgramMethod target, ProgramMethod bridge, @@ -262,7 +290,7 @@ @Override public void acceptTwrCloseResourceMethod(ProgramMethod closeMethod, ProgramMethod context) { - methodProcessor.scheduleMethodForProcessing(closeMethod, this); + methodProcessor.scheduleMethodForProcessing(closeMethod, outermostEventConsumer); } @Override @@ -326,7 +354,7 @@ } @Override - public void acceptAPIConversion(ProgramMethod method) { + public void acceptAPIConversionOutline(ProgramMethod method, ProgramMethod context) { methodProcessor.scheduleDesugaredMethodForProcessing(method); } @@ -385,7 +413,7 @@ private void finalizeConstantDynamicDesugaring(Consumer<ProgramMethod> needsProcessing) { for (ConstantDynamicClass constantDynamicClass : synthesizedConstantDynamicClasses) { - constantDynamicClass.rewriteBootstrapMethodSignatureIfNeeded(); + constantDynamicClass.rewriteBootstrapMethodSignatureIfNeeded(outermostEventConsumer); constantDynamicClass.getConstantDynamicProgramClass().forEachProgramMethod(needsProcessing); } synthesizedConstantDynamicClasses.clear(); @@ -488,7 +516,13 @@ } @Override - public void acceptCollectionConversion(ProgramMethod arrayConversion) { + public void acceptVarHandleDesugaringClassContext( + DexProgramClass clazz, ProgramDefinition context) { + // Intentionally empty. + } + + @Override + public void acceptCollectionConversion(ProgramMethod arrayConversion, ProgramMethod context) { // Intentionally empty. The method will be hit by tracing if required. } @@ -514,7 +548,7 @@ } @Override - public void acceptCovariantRetargetMethod(ProgramMethod method) { + public void acceptCovariantRetargetMethod(ProgramMethod method, ProgramMethod context) { // Intentionally empty. The method will be hit by tracing if required. } @@ -580,7 +614,7 @@ } @Override - public void acceptAPIConversion(ProgramMethod method) { + public void acceptAPIConversionOutline(ProgramMethod method, ProgramMethod context) { // Intentionally empty. The method will be hit by tracing if required. } @@ -623,6 +657,12 @@ } @Override + public void acceptConstantDynamicRewrittenBootstrapMethod( + ProgramMethod bootstrapMethod, DexMethod oldSignature) { + // Intentionally empty. + } + + @Override public void acceptNestConstructorBridge( ProgramMethod target, ProgramMethod bridge,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java index 29cb618..7a38cf1 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
@@ -3,15 +3,18 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.ir.desugar; +import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexClasspathClass; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.MethodResolutionResult.FailedResolutionResult; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.D8MethodProcessor; import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryAPICallbackSynthesizorEventConsumer; +import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor; import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterPostProcessingEventConsumer; import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper; import com.android.tools.r8.ir.desugar.itf.InterfaceProcessingDesugaringEventConsumer; @@ -35,16 +38,18 @@ DesugaredLibraryAPICallbackSynthesizorEventConsumer { public static CfPostProcessingDesugaringEventConsumer createForD8( + AppView<?> appView, ArtProfileCollectionAdditions artProfileCollectionAdditions, D8MethodProcessor methodProcessor, CfInstructionDesugaringCollection instructionDesugaring) { CfPostProcessingDesugaringEventConsumer eventConsumer = new D8CfPostProcessingDesugaringEventConsumer(methodProcessor, instructionDesugaring); return ArtProfileRewritingCfPostProcessingDesugaringEventConsumer.attach( - artProfileCollectionAdditions, eventConsumer); + appView, artProfileCollectionAdditions, eventConsumer); } public static CfPostProcessingDesugaringEventConsumer createForR8( + AppView<?> appView, SyntheticAdditions additions, ArtProfileCollectionAdditions artProfileCollectionAdditions, CfInstructionDesugaringCollection desugaring, @@ -52,7 +57,7 @@ CfPostProcessingDesugaringEventConsumer eventConsumer = new R8PostProcessingDesugaringEventConsumer(additions, desugaring, missingClassConsumer); return ArtProfileRewritingCfPostProcessingDesugaringEventConsumer.attach( - artProfileCollectionAdditions, eventConsumer); + appView, artProfileCollectionAdditions, eventConsumer); } public abstract Set<DexMethod> getNewlyLiveMethods(); @@ -93,7 +98,7 @@ } @Override - public void acceptCovariantRetargetMethod(ProgramMethod method) { + public void acceptCovariantRetargetMethod(ProgramMethod method, ProgramMethod context) { addMethodToReprocess(method); } @@ -109,7 +114,8 @@ } @Override - public void acceptDesugaredLibraryRetargeterForwardingMethod(ProgramMethod method) { + public void acceptDesugaredLibraryRetargeterForwardingMethod( + ProgramMethod method, EmulatedDispatchMethodDescriptor descriptor) { addMethodToReprocess(method); } @@ -120,12 +126,13 @@ } @Override - public void acceptThrowingMethod(ProgramMethod method, DexType errorType) { + public void acceptThrowingMethod( + ProgramMethod method, DexType errorType, FailedResolutionResult resolutionResult) { addMethodToReprocess(method); } @Override - public void acceptCollectionConversion(ProgramMethod method) { + public void acceptCollectionConversion(ProgramMethod method, ProgramMethod context) { addMethodToReprocess(method); } @@ -143,8 +150,9 @@ } @Override - public void acceptAPIConversionCallback(ProgramMethod method) { - addMethodToReprocess(method); + public void acceptAPIConversionCallback( + ProgramMethod callbackMethod, ProgramMethod convertedMethod) { + addMethodToReprocess(callbackMethod); } @Override @@ -215,12 +223,13 @@ } @Override - public void acceptCovariantRetargetMethod(ProgramMethod method) { + public void acceptCovariantRetargetMethod(ProgramMethod method, ProgramMethod context) { additions.addLiveMethod(method); } @Override - public void acceptDesugaredLibraryRetargeterForwardingMethod(ProgramMethod method) { + public void acceptDesugaredLibraryRetargeterForwardingMethod( + ProgramMethod method, EmulatedDispatchMethodDescriptor descriptor) { additions.addLiveMethod(method); } @@ -231,19 +240,21 @@ } @Override - public void acceptThrowingMethod(ProgramMethod method, DexType errorType) { + public void acceptThrowingMethod( + ProgramMethod method, DexType errorType, FailedResolutionResult resolutionResult) { additions.addLiveMethod(method); } @Override - public void acceptCollectionConversion(ProgramMethod method) { + public void acceptCollectionConversion(ProgramMethod method, ProgramMethod context) { additions.addLiveMethod(method); } @Override - public void acceptAPIConversionCallback(ProgramMethod method) { - assert !desugaring.needsDesugaring(method); - additions.addLiveMethod(method); + public void acceptAPIConversionCallback( + ProgramMethod callbackMethod, ProgramMethod convertedMethod) { + assert !desugaring.needsDesugaring(callbackMethod); + additions.addLiveMethod(callbackMethod); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java index 1143afd..788afd1 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
@@ -5,6 +5,7 @@ package com.android.tools.r8.ir.desugar; import com.android.tools.r8.errors.CompilationError; +import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexAnnotation; import com.android.tools.r8.graph.DexAnnotationElement; import com.android.tools.r8.graph.DexApplication; @@ -53,23 +54,31 @@ // the contained CovariantReturnType annotations. public final class CovariantReturnTypeAnnotationTransformer { + private final AppView<?> appView; private final IRConverter converter; - private final MethodProcessorEventConsumer eventConsumer = MethodProcessorEventConsumer.empty(); + private final MethodProcessorEventConsumer methodProcessorEventConsumer = + MethodProcessorEventConsumer.empty(); private final DexItemFactory factory; - public CovariantReturnTypeAnnotationTransformer(IRConverter converter, DexItemFactory factory) { + public CovariantReturnTypeAnnotationTransformer(AppView<?> appView, IRConverter converter) { + this.appView = appView; this.converter = converter; - this.factory = factory; + this.factory = appView.dexItemFactory(); } - public void process(DexApplication.Builder<?> builder) { + public void process( + DexApplication.Builder<?> builder, + CovariantReturnTypeAnnotationTransformerEventConsumer eventConsumer) { // List of methods that should be added to the next class. List<DexEncodedMethod> methodsWithCovariantReturnTypeAnnotation = new LinkedList<>(); List<DexEncodedMethod> covariantReturnTypeMethods = new LinkedList<>(); for (DexProgramClass clazz : builder.getProgramClasses()) { // Construct the methods that should be added to clazz. buildCovariantReturnTypeMethodsForClass( - clazz, methodsWithCovariantReturnTypeAnnotation, covariantReturnTypeMethods); + clazz, + methodsWithCovariantReturnTypeAnnotation, + covariantReturnTypeMethods, + eventConsumer); if (covariantReturnTypeMethods.isEmpty()) { continue; } @@ -111,12 +120,14 @@ private void buildCovariantReturnTypeMethodsForClass( DexProgramClass clazz, List<DexEncodedMethod> methodsWithCovariantReturnTypeAnnotation, - List<DexEncodedMethod> covariantReturnTypeMethods) { + List<DexEncodedMethod> covariantReturnTypeMethods, + CovariantReturnTypeAnnotationTransformerEventConsumer eventConsumer) { clazz.forEachProgramVirtualMethod( method -> { if (methodHasCovariantReturnTypeAnnotation(method.getDefinition())) { methodsWithCovariantReturnTypeAnnotation.add(method.getDefinition()); - buildCovariantReturnTypeMethodsForMethod(method, covariantReturnTypeMethods); + buildCovariantReturnTypeMethodsForMethod( + method, covariantReturnTypeMethods, eventConsumer); } }); } @@ -134,11 +145,13 @@ // variantReturnTypes annotations on the given method. Adds the newly constructed, synthetic // methods to the list covariantReturnTypeMethods. private void buildCovariantReturnTypeMethodsForMethod( - ProgramMethod method, List<DexEncodedMethod> covariantReturnTypeMethods) { + ProgramMethod method, + List<DexEncodedMethod> covariantReturnTypeMethods, + CovariantReturnTypeAnnotationTransformerEventConsumer eventConsumer) { assert methodHasCovariantReturnTypeAnnotation(method.getDefinition()); for (DexType covariantReturnType : getCovariantReturnTypes(method)) { DexEncodedMethod covariantReturnTypeMethod = - buildCovariantReturnTypeMethod(method, covariantReturnType); + buildCovariantReturnTypeMethod(method, covariantReturnType, eventConsumer); covariantReturnTypeMethods.add(covariantReturnTypeMethod); } } @@ -149,7 +162,9 @@ // // Note: any "synchronized" or "strictfp" modifier could be dropped safely. private DexEncodedMethod buildCovariantReturnTypeMethod( - ProgramMethod method, DexType covariantReturnType) { + ProgramMethod method, + DexType covariantReturnType, + CovariantReturnTypeAnnotationTransformerEventConsumer eventConsumer) { DexProgramClass methodHolder = method.getHolder(); DexMethod methodReference = method.getReference(); DexEncodedMethod methodDefinition = method.getDefinition(); @@ -183,7 +198,8 @@ .build(); // Optimize to generate DexCode instead of CfCode. ProgramMethod programMethod = new ProgramMethod(methodHolder, newVirtualMethod); - converter.optimizeSynthesizedMethod(programMethod, eventConsumer); + converter.optimizeSynthesizedMethod(programMethod, methodProcessorEventConsumer); + eventConsumer.acceptCovariantReturnTypeBridgeMethod(programMethod, method); return newVirtualMethod; }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformerEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformerEventConsumer.java new file mode 100644 index 0000000..dc4add0 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformerEventConsumer.java
@@ -0,0 +1,45 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir.desugar; + +import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions; +import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer; + +public interface CovariantReturnTypeAnnotationTransformerEventConsumer { + + void acceptCovariantReturnTypeBridgeMethod(ProgramMethod bridge, ProgramMethod target); + + static CovariantReturnTypeAnnotationTransformerEventConsumer create( + ArtProfileCollectionAdditions artProfileCollectionAdditions) { + if (artProfileCollectionAdditions.isNop()) { + return empty(); + } + return ArtProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer.attach( + artProfileCollectionAdditions, empty()); + } + + static EmptyCovariantReturnTypeAnnotationTransformerEventConsumer empty() { + return EmptyCovariantReturnTypeAnnotationTransformerEventConsumer.getInstance(); + } + + class EmptyCovariantReturnTypeAnnotationTransformerEventConsumer + implements CovariantReturnTypeAnnotationTransformerEventConsumer { + + private static final EmptyCovariantReturnTypeAnnotationTransformerEventConsumer INSTANCE = + new EmptyCovariantReturnTypeAnnotationTransformerEventConsumer(); + + private EmptyCovariantReturnTypeAnnotationTransformerEventConsumer() {} + + static EmptyCovariantReturnTypeAnnotationTransformerEventConsumer getInstance() { + return INSTANCE; + } + + @Override + public void acceptCovariantReturnTypeBridgeMethod(ProgramMethod bridge, ProgramMethod target) { + // Intentionally empty. + } + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java index 292d844..5f65c95 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
@@ -94,25 +94,8 @@ if (context.getDefinition().isD8R8Synthesized()) { return appView.computedMinApiLevel(); } - DexReference reference; - if (instruction.isInvoke()) { - CfInvoke cfInvoke = instruction.asInvoke(); - if (cfInvoke.isInvokeSpecial()) { - return appView.computedMinApiLevel(); - } - reference = cfInvoke.getMethod(); - } else if (instruction.isFieldInstruction()) { - reference = instruction.asFieldInstruction().getField(); - } else if (instruction.isCheckCast()) { - reference = instruction.asCheckCast().getType(); - } else if (instruction.isInstanceOf()) { - reference = instruction.asInstanceOf().getType(); - } else if (instruction.isConstClass()) { - reference = instruction.asConstClass().getType(); - } else { - return appView.computedMinApiLevel(); - } - if (!reference.getContextType().isClassType()) { + DexReference reference = getReferenceFromInstruction(instruction); + if (reference == null || !reference.getContextType().isClassType()) { return appView.computedMinApiLevel(); } DexClass holder = appView.definitionFor(reference.getContextType()); @@ -158,6 +141,22 @@ } } + private DexReference getReferenceFromInstruction(CfInstruction instruction) { + if (instruction.isFieldInstruction()) { + return instruction.asFieldInstruction().getField(); + } else if (instruction.isCheckCast()) { + return instruction.asCheckCast().getType(); + } else if (instruction.isInstanceOf()) { + return instruction.asInstanceOf().getType(); + } else if (instruction.isConstClass()) { + return instruction.asConstClass().getType(); + } else if (instruction.isInvoke() && !instruction.asInvoke().isInvokeSpecial()) { + return instruction.asInvoke().getMethod(); + } else { + return null; + } + } + private DexEncodedMember<?, ?> simpleLookupInClassHierarchy( DexLibraryClass holder, Function<DexClass, DexEncodedMember<?, ?>> lookup) { DexEncodedMember<?, ?> result = lookup.apply(holder); @@ -208,10 +207,20 @@ ComputedApiLevel apiLevel, DexItemFactory factory, ProgramMethod programContext) { + DexReference reference = getReferenceFromInstruction(instruction); + assert reference != null; + DexClass holder = appView.definitionFor(reference.getContextType()); + assert holder != null; return appView .getSyntheticItems() .createMethod( - kinds -> kinds.API_MODEL_OUTLINE, + kinds -> + // We've already checked that the definition the reference is targeting is public + // when computing the api-level for desugaring. We still have to ensure that the + // class cannot be merged globally if it is package private. + holder.isPublic() + ? kinds.API_MODEL_OUTLINE + : kinds.API_MODEL_OUTLINE_WITHOUT_GLOBAL_MERGING, context, appView, syntheticMethodBuilder -> {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java index 5105707..161262d 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
@@ -355,13 +355,14 @@ this.clazz = clazz; } - public void rewriteBootstrapMethodSignatureIfNeeded() { + public void rewriteBootstrapMethodSignatureIfNeeded( + ConstantDynamicDesugaringEventConsumer eventConsumer) { if (!shouldRewriteBootstrapMethodSignature() || behaviour != CACHE_CONSTANT) { return; } DexProgramClass bootstrapMethodHolder = appView.definitionFor(bootstrapMethodReference.getHolderType()).asProgramClass(); - DexEncodedMethod replacement = + DexEncodedMethod finalDefinition = bootstrapMethodHolder .getMethodCollection() .replaceDirectMethod( @@ -385,18 +386,22 @@ newMethod.copyMetadata(appView, encodedMethod); return newMethod; }); - if (replacement != null) { + ProgramMethod finalMethod; + if (finalDefinition != null) { // Since we've copied the code object from an existing method, the code should already be // processed, and thus we don't need to schedule it for processing in D8. - assert !appView.options().isGeneratingClassFiles() || replacement.getCode().isCfCode(); - assert !appView.options().isGeneratingDex() || replacement.getCode().isDexCode(); + assert !appView.options().isGeneratingClassFiles() || finalDefinition.getCode().isCfCode(); + assert !appView.options().isGeneratingDex() || finalDefinition.getCode().isDexCode(); + finalMethod = finalDefinition.asProgramMethod(bootstrapMethodHolder); + eventConsumer.acceptConstantDynamicRewrittenBootstrapMethod( + finalMethod, bootstrapMethodReference); + } else { + finalMethod = bootstrapMethodHolder.lookupProgramMethod(finalBootstrapMethodReference); } // The method might already have been moved by another dynamic constant targeting it. // If so, it must be defined on the holder. - ProgramMethod modified = - bootstrapMethodHolder.lookupProgramMethod(finalBootstrapMethodReference); - assert modified != null; - assert modified.getDefinition().isPublicMethod(); + assert finalMethod != null; + assert finalMethod.getDefinition().isPublicMethod(); } private DexType mapLookupTypeToObject(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicDesugaringEventConsumer.java index 052b8b9..d487ccb 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicDesugaringEventConsumer.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicDesugaringEventConsumer.java
@@ -3,11 +3,15 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.ir.desugar.constantdynamic; +import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizationsEventConsumer; public interface ConstantDynamicDesugaringEventConsumer extends UtilityMethodsForCodeOptimizationsEventConsumer { - void acceptConstantDynamicClass(ConstantDynamicClass lambdaClass, ProgramMethod context); + void acceptConstantDynamicClass(ConstantDynamicClass constantDynamicClass, ProgramMethod context); + + void acceptConstantDynamicRewrittenBootstrapMethod( + ProgramMethod bootstrapMethod, DexMethod oldSignature); }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryConversionCfProvider.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryConversionCfProvider.java index 7907037..9a75734 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryConversionCfProvider.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryConversionCfProvider.java
@@ -213,9 +213,9 @@ if (method.getDefinition().isLibraryMethodOverride().isTrue()) { newMethod.setLibraryMethodOverride(OptionalBool.TRUE); } - ProgramMethod callback = new ProgramMethod(clazz, newMethod); + ProgramMethod callback = newMethod.asProgramMethod(clazz); assert eventConsumer != null; - eventConsumer.acceptAPIConversionCallback(callback); + eventConsumer.acceptAPIConversionCallback(callback, method); return callback; } @@ -260,7 +260,7 @@ parameterConversions, invoke.getOpcode()) .generateCfCode())); - eventConsumer.acceptAPIConversion(outline); + eventConsumer.acceptAPIConversionOutline(outline, context); return outline; } @@ -362,7 +362,8 @@ methodSignature -> computeParameterConversionCfCode( methodSignature.holder, invokedMethod, parameterConversions))); - eventConsumer.acceptAPIConversion(parameterConversion); + eventConsumer.acceptAPIConversionOutline( + parameterConversion, methodProcessingContext.getMethodContext()); cfInstructions.add( new CfInvoke(Opcodes.INVOKESTATIC, parameterConversion.getReference(), false)); int arrayLocal = freshLocalProvider.getFreshLocal(ValueType.OBJECT.requiredRegisters()); @@ -461,6 +462,7 @@ destIsVivified, apiConversionCollection, eventConsumer, + context, contextSupplier), context); } @@ -479,6 +481,7 @@ destIsVivified, apiConversionCollection, eventConsumer, + context, contextSupplier), context); } @@ -507,7 +510,12 @@ wrapperSynthesizer, (argType, apiGenericTypesConversion) -> wrapperSynthesizer.ensureConversionMethod( - argType, destIsVivified, apiGenericTypesConversion, eventConsumer, contextSupplier), + argType, + destIsVivified, + apiGenericTypesConversion, + eventConsumer, + context, + contextSupplier), context); } @@ -522,7 +530,12 @@ wrapperSynthesizer, (argType, apiGenericTypesConversion) -> wrapperSynthesizer.getExistingProgramConversionMethod( - argType, destIsVivified, apiGenericTypesConversion, eventConsumer, contextSupplier), + argType, + destIsVivified, + apiGenericTypesConversion, + eventConsumer, + context, + contextSupplier), context); }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java index 40f9d67..cc5d7d6 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
@@ -197,6 +197,7 @@ boolean destIsVivified, DexMethod apiGenericTypesConversion, DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer, + ProgramMethod context, Supplier<UniqueContext> contextSupplier) { if (apiGenericTypesConversion != null) { assert !type.isArrayType(); @@ -205,7 +206,8 @@ DexType srcType = destIsVivified ? type : vivifiedTypeFor(type); DexType destType = destIsVivified ? vivifiedTypeFor(type) : type; if (type.isArrayType()) { - return ensureArrayConversionMethod(type, srcType, destType, eventConsumer, contextSupplier); + return ensureArrayConversionMethod( + type, srcType, destType, eventConsumer, context, contextSupplier); } DexMethod customConversion = getCustomConversion(type, srcType, destType); if (customConversion != null) { @@ -231,6 +233,7 @@ DexType srcType, DexType destType, DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer, + ProgramMethod context, Supplier<UniqueContext> contextSupplier) { DexMethod conversion = ensureConversionMethod( @@ -238,9 +241,10 @@ srcType == type, null, eventConsumer, + context, contextSupplier); return ensureArrayConversionMethod( - srcType, destType, eventConsumer, contextSupplier, conversion); + srcType, destType, eventConsumer, context, contextSupplier, conversion); } private DexMethod ensureArrayConversionMethodFromExistingBaseConversion( @@ -248,6 +252,7 @@ DexType srcType, DexType destType, DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer, + ProgramMethod context, Supplier<UniqueContext> contextSupplier) { DexMethod conversion = getExistingProgramConversionMethod( @@ -255,15 +260,17 @@ srcType == type, null, eventConsumer, + context, contextSupplier); return ensureArrayConversionMethod( - srcType, destType, eventConsumer, contextSupplier, conversion); + srcType, destType, eventConsumer, context, contextSupplier, conversion); } private DexMethod ensureArrayConversionMethod( DexType srcType, DexType destType, DesugaredLibraryWrapperSynthesizerEventConsumer eventConsumer, + ProgramMethod context, Supplier<UniqueContext> contextSupplier, DexMethod conversion) { ProgramMethod arrayConversion = @@ -286,7 +293,7 @@ destType, conversion) .generateCfCode())); - eventConsumer.acceptCollectionConversion(arrayConversion); + eventConsumer.acceptCollectionConversion(arrayConversion, context); return arrayConversion.getReference(); } @@ -295,6 +302,7 @@ boolean destIsVivified, DexMethod apiGenericTypesConversion, DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer, + ProgramMethod context, Supplier<UniqueContext> contextSupplier) { if (apiGenericTypesConversion != null) { assert !type.isArrayType(); @@ -304,7 +312,7 @@ DexType destType = destIsVivified ? vivifiedTypeFor(type) : type; if (type.isArrayType()) { return ensureArrayConversionMethodFromExistingBaseConversion( - type, srcType, destType, eventConsumer, contextSupplier); + type, srcType, destType, eventConsumer, context, contextSupplier); } DexMethod customConversion = getCustomConversion(type, srcType, destType); if (customConversion != null) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizerEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizerEventConsumer.java index 4adc11f..9a57f6b 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizerEventConsumer.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizerEventConsumer.java
@@ -10,7 +10,7 @@ public interface DesugaredLibraryWrapperSynthesizerEventConsumer { - void acceptCollectionConversion(ProgramMethod arrayConversion); + void acceptCollectionConversion(ProgramMethod arrayConversion, ProgramMethod context); interface DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer extends DesugaredLibraryWrapperSynthesizerEventConsumer { @@ -33,12 +33,12 @@ interface DesugaredLibraryAPIConverterEventConsumer extends DesugaredLibraryClasspathWrapperSynthesizeEventConsumer { - void acceptAPIConversion(ProgramMethod method); + void acceptAPIConversionOutline(ProgramMethod method, ProgramMethod context); } interface DesugaredLibraryAPICallbackSynthesizorEventConsumer extends DesugaredLibraryClasspathWrapperSynthesizeEventConsumer { - void acceptAPIConversionCallback(ProgramMethod method); + void acceptAPIConversionCallback(ProgramMethod callbackMethod, ProgramMethod convertedMethod); } }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java index 859bc95..df5c8d0 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java
@@ -6,7 +6,6 @@ import com.android.tools.r8.graph.CfCode.LocalVariableInfo; import com.android.tools.r8.graph.ClassAccessFlags; -import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexProto; @@ -14,6 +13,7 @@ import com.android.tools.r8.graph.FieldAccessFlags; import com.android.tools.r8.graph.MethodAccessFlags; import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.ClassAnnotation; +import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.FieldAnnotation; import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.MethodAnnotation; import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.SupportedClass; import com.android.tools.r8.utils.AndroidApiLevel; @@ -24,7 +24,6 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; -import java.util.stream.StreamSupport; public class GenerateHtmlDoc extends AbstractGenerateFiles { @@ -91,8 +90,8 @@ private abstract static class SourceBuilder<B extends GenerateHtmlDoc.SourceBuilder> { - protected final DexClass clazz; - protected List<DexEncodedField> fields = new ArrayList<>(); + protected Map<DexEncodedField, FieldAnnotation> fields = + new TreeMap<>(Comparator.comparing(DexEncodedField::getReference)); protected Map<DexEncodedMethod, MethodAnnotation> constructors = new TreeMap<>(Comparator.comparing(DexEncodedMethod::getReference)); protected Map<DexEncodedMethod, MethodAnnotation> methods = @@ -101,17 +100,16 @@ String className; String packageName; - private SourceBuilder(DexClass clazz) { - this.clazz = clazz; - this.className = clazz.type.toSourceString(); + private SourceBuilder(DexType classType) { + this.className = classType.toSourceString(); int index = this.className.lastIndexOf('.'); this.packageName = index > 0 ? this.className.substring(0, index) : ""; } public abstract B self(); - private B addField(DexEncodedField field) { - fields.add(field); + private B addField(DexEncodedField field, FieldAnnotation fieldAnnotation) { + fields.put(field, fieldAnnotation); return self(); } @@ -386,8 +384,8 @@ private boolean unsupportedInMinApiRange = false; private boolean covariantReturnSupported = false; - public HTMLSourceBuilder(DexClass clazz, ClassAnnotation classAnnotation) { - super(clazz); + public HTMLSourceBuilder(DexType classType, ClassAnnotation classAnnotation) { + super(classType); this.classAnnotation = classAnnotation; } @@ -396,6 +394,18 @@ return this; } + private String getTextAnnotations(FieldAnnotation annotation) { + if (annotation == null) { + return ""; + } + StringBuilder stringBuilder = new StringBuilder(); + if (annotation.unsupportedInMinApiRange) { + stringBuilder.append(SUP_3); + unsupportedInMinApiRange = true; + } + return stringBuilder.toString(); + } + private String getTextAnnotations(MethodAnnotation annotation) { if (annotation == null) { return ""; @@ -434,13 +444,14 @@ "ul style=\"list-style-position:inside; list-style-type: none !important;" + " margin-left:0px;padding-left:0px !important;\""); if (!fields.isEmpty()) { - for (DexEncodedField field : fields) { + for (DexEncodedField field : fields.keySet()) { builder.appendLiCode( accessFlags(field.accessFlags) + " " + typeInPackage(field.getReference().type) + " " - + field.getReference().name); + + field.getReference().name + + getTextAnnotations(fields.get(field))); } } if (!constructors.isEmpty()) { @@ -470,16 +481,19 @@ if (classAnnotation.isFullySupported()) { commentBuilder.append("Fully implemented class.").append(HTML_SPLIT); } + if (classAnnotation.isAdditionalMembersOnClass()) { + commentBuilder.append("Additional methods on existing class.").append(HTML_SPLIT); + } if (parallelStreamMethod) { commentBuilder .append(SUP_1) - .append("Supported only on devices which API level is 21 or higher.") + .append(" Supported only on devices which API level is 21 or higher.") .append(HTML_SPLIT); } if (missingFromLatestAndroidJar) { commentBuilder .append(SUP_2) - .append("Not present in Android ") + .append(" Not present in Android ") .append(MAX_TESTED_ANDROID_API_LEVEL) .append(" (May not resolve at compilation).") .append(HTML_SPLIT); @@ -511,18 +525,13 @@ } private void generateClassHTML(PrintStream ps, SupportedClass supportedClass) { - DexClass clazz = supportedClass.getClazz(); + DexType classType = supportedClass.getType(); SourceBuilder<HTMLSourceBuilder> builder = - new HTMLSourceBuilder(clazz, supportedClass.getClassAnnotation()); - // We need to extend to support fields. - StreamSupport.stream(clazz.fields().spliterator(), false) - .filter(field -> field.accessFlags.isPublic() || field.accessFlags.isProtected()) - .sorted(Comparator.comparing(DexEncodedField::toSourceString)) - .forEach(builder::addField); + new HTMLSourceBuilder(classType, supportedClass.getClassAnnotation()); + supportedClass.forEachFieldAndAnnotation(builder::addField); supportedClass.forEachMethodAndAnnotation( (method, methodAnnotation) -> { - if ((method.accessFlags.isPublic() || method.accessFlags.isProtected()) - && !method.accessFlags.isBridge()) { + if (!method.accessFlags.isBridge()) { builder.addMethod(method, methodAnnotation); } }); @@ -531,10 +540,15 @@ @Override AndroidApiLevel run() throws Exception { - PrintStream ps = new PrintStream(Files.newOutputStream(outputDirectory.resolve("apis.html"))); + return run("apis.html"); + } + + public AndroidApiLevel run(String outputFileName) throws Exception { + PrintStream ps = + new PrintStream(Files.newOutputStream(outputDirectory.resolve(outputFileName))); SupportedClasses supportedClasses = - new SupportedMethodsGenerator(options) + new SupportedClassesGenerator(options) .run(desugaredLibraryImplementation, desugaredLibrarySpecificationPath); // Full classes added.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java index b7a1a27..63dec3c 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java
@@ -76,7 +76,7 @@ } private void addMethodsToHeaderJar( - DexApplication.Builder builder, DexClass clazz, List<DexEncodedMethod> methods) { + DexApplication.Builder builder, DexClass clazz, Collection<DexEncodedMethod> methods) { if (methods.size() == 0) { return; } @@ -244,7 +244,7 @@ AndroidApiLevel compilationLevel = desugaredLibrarySpecification.getRequiredCompilationApiLevel(); SupportedClasses supportedMethods = - new SupportedMethodsGenerator(options) + new SupportedClassesGenerator(options) .run(desugaredLibraryImplementation, desugaredLibrarySpecificationPath); System.out.println("Generating lint files for compile API " + compilationLevel); generateLintFiles(compilationLevel, AndroidApiLevel.B, supportedMethods);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClasses.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClasses.java index 3c1cfd6..f6eb4e4 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClasses.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClasses.java
@@ -5,17 +5,20 @@ package com.android.tools.r8.ir.desugar.desugaredlibrary.lint; import com.android.tools.r8.graph.DexClass; +import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexType; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSortedMap; -import java.util.ArrayList; +import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; +import java.util.SortedMap; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -34,18 +37,27 @@ private final DexClass clazz; private final ClassAnnotation classAnnotation; - private final List<DexEncodedMethod> supportedMethods; + // We need the encoded version to be able to access the flags and infer parameter names from + // the debug info. However, we analyze multiple applications and the encoded versions are not + // unique, but the keys are. + private final SortedMap<DexMethod, DexEncodedMethod> supportedMethods; + private final SortedMap<DexField, DexEncodedField> supportedFields; private final Map<DexMethod, MethodAnnotation> methodAnnotations; + private final Map<DexField, FieldAnnotation> fieldAnnotations; private SupportedClass( DexClass clazz, ClassAnnotation classAnnotation, - List<DexEncodedMethod> supportedMethods, - Map<DexMethod, MethodAnnotation> methodAnnotations) { + SortedMap<DexMethod, DexEncodedMethod> supportedMethods, + SortedMap<DexField, DexEncodedField> supportedFields, + Map<DexMethod, MethodAnnotation> methodAnnotations, + Map<DexField, FieldAnnotation> fieldAnnotations) { this.clazz = clazz; this.classAnnotation = classAnnotation; this.supportedMethods = supportedMethods; + this.supportedFields = supportedFields; this.methodAnnotations = methodAnnotations; + this.fieldAnnotations = fieldAnnotations; } public DexType getType() { @@ -60,13 +72,14 @@ return classAnnotation; } - public List<DexEncodedMethod> getSupportedMethods() { - return supportedMethods; + public Collection<DexEncodedMethod> getSupportedMethods() { + // Since the map is sorted, the values are sorted. + return supportedMethods.values(); } public void forEachMethodAndAnnotation( BiConsumer<DexEncodedMethod, MethodAnnotation> biConsumer) { - for (DexEncodedMethod supportedMethod : supportedMethods) { + for (DexEncodedMethod supportedMethod : supportedMethods.values()) { biConsumer.accept(supportedMethod, getMethodAnnotation(supportedMethod.getReference())); } } @@ -75,6 +88,16 @@ return methodAnnotations.get(method); } + public void forEachFieldAndAnnotation(BiConsumer<DexEncodedField, FieldAnnotation> biConsumer) { + for (DexEncodedField supportedField : supportedFields.values()) { + biConsumer.accept(supportedField, getFieldAnnotation(supportedField.getReference())); + } + } + + public FieldAnnotation getFieldAnnotation(DexField field) { + return fieldAnnotations.get(field); + } + static Builder builder(DexClass clazz) { return new Builder(clazz); } @@ -83,31 +106,44 @@ private final DexClass clazz; private ClassAnnotation classAnnotation; - private final List<DexEncodedMethod> supportedMethods = new ArrayList<>(); + private final Map<DexMethod, DexEncodedMethod> supportedMethods = new IdentityHashMap<>(); + private final Map<DexField, DexEncodedField> supportedFields = new IdentityHashMap<>(); private final Map<DexMethod, MethodAnnotation> methodAnnotations = new HashMap<>(); + private final Map<DexField, FieldAnnotation> fieldAnnotations = new HashMap<>(); private Builder(DexClass clazz) { this.clazz = clazz; } - void forEachMethods(BiConsumer<DexClass, List<DexEncodedMethod>> biConsumer) { - biConsumer.accept(clazz, supportedMethods); + void forEachMethods(BiConsumer<DexClass, Collection<DexEncodedMethod>> biConsumer) { + biConsumer.accept(clazz, supportedMethods.values()); } void forEachMethod(BiConsumer<DexClass, DexEncodedMethod> biConsumer) { - for (DexEncodedMethod dexEncodedMethod : supportedMethods) { + for (DexEncodedMethod dexEncodedMethod : supportedMethods.values()) { biConsumer.accept(clazz, dexEncodedMethod); } } + void forEachField(BiConsumer<DexClass, DexEncodedField> biConsumer) { + for (DexEncodedField dexEncodedField : supportedFields.values()) { + biConsumer.accept(clazz, dexEncodedField); + } + } + void addSupportedMethod(DexEncodedMethod method) { assert method.getHolderType() == clazz.type; - supportedMethods.add(method); + supportedMethods.put(method.getReference(), method); + } + + void addSupportedField(DexEncodedField field) { + assert field.getHolderType() == clazz.type; + supportedFields.put(field.getReference(), field); } void annotateClass(ClassAnnotation annotation) { assert annotation != null; - assert classAnnotation == null; + assert classAnnotation == null || annotation == classAnnotation; classAnnotation = annotation; } @@ -118,14 +154,24 @@ methodAnnotations.put(method, annotation.combine(prev)); } + void annotateField(DexField field, FieldAnnotation annotation) { + assert field.getHolderType() == clazz.type; + FieldAnnotation prev = fieldAnnotations.getOrDefault(field, FieldAnnotation.getDefault()); + fieldAnnotations.put(field, annotation.combine(prev)); + } + MethodAnnotation getMethodAnnotation(DexMethod method) { return methodAnnotations.get(method); } SupportedClass build() { - supportedMethods.sort(Comparator.comparing(DexEncodedMethod::getReference)); return new SupportedClass( - clazz, classAnnotation, ImmutableList.copyOf(supportedMethods), methodAnnotations); + clazz, + classAnnotation, + ImmutableSortedMap.copyOf(supportedMethods), + ImmutableSortedMap.copyOf(supportedFields), + methodAnnotations, + fieldAnnotations); } } } @@ -138,7 +184,13 @@ Map<DexType, SupportedClass.Builder> supportedClassBuilders = new IdentityHashMap<>(); - void forEachClassAndMethods(BiConsumer<DexClass, List<DexEncodedMethod>> biConsumer) { + ClassAnnotation getClassAnnotation(DexType type) { + SupportedClass.Builder builder = supportedClassBuilders.get(type); + assert builder != null; + return builder.classAnnotation; + } + + void forEachClassAndMethods(BiConsumer<DexClass, Collection<DexEncodedMethod>> biConsumer) { supportedClassBuilders .values() .forEach(classBuilder -> classBuilder.forEachMethods(biConsumer)); @@ -150,6 +202,12 @@ .forEach(classBuilder -> classBuilder.forEachMethod(biConsumer)); } + void forEachClassAndField(BiConsumer<DexClass, DexEncodedField> biConsumer) { + supportedClassBuilders + .values() + .forEach(classBuilder -> classBuilder.forEachField(biConsumer)); + } + void addSupportedMethod(DexClass holder, DexEncodedMethod method) { SupportedClass.Builder classBuilder = supportedClassBuilders.computeIfAbsent( @@ -157,6 +215,13 @@ classBuilder.addSupportedMethod(method); } + void addSupportedField(DexClass holder, DexEncodedField field) { + SupportedClass.Builder classBuilder = + supportedClassBuilders.computeIfAbsent( + holder.type, clazz -> SupportedClass.builder(holder)); + classBuilder.addSupportedField(field); + } + void annotateClass(DexType type, ClassAnnotation annotation) { SupportedClass.Builder classBuilder = supportedClassBuilders.get(type); assert classBuilder != null; @@ -169,6 +234,12 @@ classBuilder.annotateMethod(method, annotation); } + void annotateField(DexField field, FieldAnnotation annotation) { + SupportedClass.Builder classBuilder = supportedClassBuilders.get(field.getHolderType()); + assert classBuilder != null; + classBuilder.annotateField(field, annotation); + } + void annotateMethodIfPresent(DexMethod method, MethodAnnotation annotation) { SupportedClass.Builder classBuilder = supportedClassBuilders.get(method.getHolderType()); if (classBuilder == null) { @@ -195,16 +266,34 @@ static class ClassAnnotation { + private final boolean additionalMembersOnClass; private final boolean fullySupported; // Methods in latest android.jar but unsupported. private final List<DexMethod> unsupportedMethods; public ClassAnnotation(boolean fullySupported, List<DexMethod> unsupportedMethods) { + this.additionalMembersOnClass = false; this.fullySupported = fullySupported; unsupportedMethods.sort(Comparator.naturalOrder()); this.unsupportedMethods = ImmutableList.copyOf(unsupportedMethods); } + private ClassAnnotation() { + this.additionalMembersOnClass = true; + this.fullySupported = false; + this.unsupportedMethods = ImmutableList.of(); + } + + private static final ClassAnnotation ADDITIONNAL_MEMBERS_ON_CLASS = new ClassAnnotation(); + + public static ClassAnnotation getAdditionnalMembersOnClass() { + return ADDITIONNAL_MEMBERS_ON_CLASS; + } + + public boolean isAdditionalMembersOnClass() { + return additionalMembersOnClass; + } + public boolean isFullySupported() { return fullySupported; } @@ -214,62 +303,17 @@ } } - public static class MethodAnnotation { - - private static final MethodAnnotation COVARIANT_RETURN_SUPPORTED = - new MethodAnnotation(false, false, true, false, -1, -1); - private static final MethodAnnotation DEFAULT = - new MethodAnnotation(false, false, false, false, -1, -1); - private static final MethodAnnotation PARALLEL_STREAM_METHOD = - new MethodAnnotation(true, false, false, false, -1, -1); - private static final MethodAnnotation MISSING_FROM_LATEST_ANDROID_JAR = - new MethodAnnotation(false, true, false, false, -1, -1); - - // ParallelStream methods are not supported when the runtime api level is strictly below 21. - final boolean parallelStreamMethod; - // Methods not in the latest android jar but still fully supported. - final boolean missingFromLatestAndroidJar; - // Methods not supported in a given min api range. + public abstract static class MemberAnnotation { final boolean unsupportedInMinApiRange; - final boolean covariantReturnSupported; final int minRange; final int maxRange; - MethodAnnotation( - boolean parallelStreamMethod, - boolean missingFromLatestAndroidJar, - boolean covariantReturnSupported, - boolean unsupportedInMinApiRange, - int minRange, - int maxRange) { - this.parallelStreamMethod = parallelStreamMethod; - this.missingFromLatestAndroidJar = missingFromLatestAndroidJar; - this.covariantReturnSupported = covariantReturnSupported; + MemberAnnotation(boolean unsupportedInMinApiRange, int minRange, int maxRange) { this.unsupportedInMinApiRange = unsupportedInMinApiRange; this.minRange = minRange; this.maxRange = maxRange; } - public static MethodAnnotation getCovariantReturnSupported() { - return COVARIANT_RETURN_SUPPORTED; - } - - public static MethodAnnotation getDefault() { - return DEFAULT; - } - - public static MethodAnnotation getParallelStreamMethod() { - return PARALLEL_STREAM_METHOD; - } - - public static MethodAnnotation getMissingFromLatestAndroidJar() { - return MISSING_FROM_LATEST_ANDROID_JAR; - } - - public static MethodAnnotation createMissingInMinApi(int api) { - return new MethodAnnotation(false, false, false, true, api, api); - } - public boolean isUnsupportedInMinApiRange() { return unsupportedInMinApiRange; } @@ -282,17 +326,7 @@ return maxRange; } - public boolean isCovariantReturnSupported() { - return covariantReturnSupported; - } - - public MethodAnnotation combine(MethodAnnotation other) { - if (this == getDefault()) { - return other; - } - if (other == getDefault()) { - return this; - } + int combineRange(MemberAnnotation other) { int newMin, newMax; if (!unsupportedInMinApiRange && !other.unsupportedInMinApiRange) { newMin = newMax = -1; @@ -320,6 +354,106 @@ } } } + assert newMax < (1 << 15) && newMin < (1 << 15); + return (newMax << 16) + newMin; + } + } + + public static class FieldAnnotation extends MemberAnnotation { + + private static final FieldAnnotation DEFAULT = new FieldAnnotation(false, -1, -1); + + FieldAnnotation(boolean unsupportedInMinApiRange, int minRange, int maxRange) { + super(unsupportedInMinApiRange, minRange, maxRange); + } + + public static FieldAnnotation getDefault() { + return DEFAULT; + } + + public static FieldAnnotation createMissingInMinApi(int api) { + return new FieldAnnotation(true, api, api); + } + + public FieldAnnotation combine(FieldAnnotation other) { + if (this == getDefault()) { + return other; + } + if (other == getDefault()) { + return this; + } + int newRange = combineRange(other); + int newMax = newRange >> 16; + int newMin = newRange & 0xFF; + return new FieldAnnotation( + unsupportedInMinApiRange || other.unsupportedInMinApiRange, newMin, newMax); + } + } + + public static class MethodAnnotation extends MemberAnnotation { + + private static final MethodAnnotation COVARIANT_RETURN_SUPPORTED = + new MethodAnnotation(false, false, true, false, -1, -1); + private static final MethodAnnotation DEFAULT = + new MethodAnnotation(false, false, false, false, -1, -1); + private static final MethodAnnotation PARALLEL_STREAM_METHOD = + new MethodAnnotation(true, false, false, false, -1, -1); + private static final MethodAnnotation MISSING_FROM_LATEST_ANDROID_JAR = + new MethodAnnotation(false, true, false, false, -1, -1); + + // ParallelStream methods are not supported when the runtime api level is strictly below 21. + final boolean parallelStreamMethod; + // Methods not in the latest android jar but still fully supported. + final boolean missingFromLatestAndroidJar; + // Methods not supported in a given min api range. + final boolean covariantReturnSupported; + MethodAnnotation( + boolean parallelStreamMethod, + boolean missingFromLatestAndroidJar, + boolean covariantReturnSupported, + boolean unsupportedInMinApiRange, + int minRange, + int maxRange) { + super(unsupportedInMinApiRange, minRange, maxRange); + this.parallelStreamMethod = parallelStreamMethod; + this.missingFromLatestAndroidJar = missingFromLatestAndroidJar; + this.covariantReturnSupported = covariantReturnSupported; + } + + public static MethodAnnotation getCovariantReturnSupported() { + return COVARIANT_RETURN_SUPPORTED; + } + + public static MethodAnnotation getDefault() { + return DEFAULT; + } + + public static MethodAnnotation getParallelStreamMethod() { + return PARALLEL_STREAM_METHOD; + } + + public static MethodAnnotation getMissingFromLatestAndroidJar() { + return MISSING_FROM_LATEST_ANDROID_JAR; + } + + public static MethodAnnotation createMissingInMinApi(int api) { + return new MethodAnnotation(false, false, false, true, api, api); + } + + public boolean isCovariantReturnSupported() { + return covariantReturnSupported; + } + + public MethodAnnotation combine(MethodAnnotation other) { + if (this == getDefault()) { + return other; + } + if (other == getDefault()) { + return this; + } + int newRange = combineRange(other); + int newMax = newRange >> 16; + int newMin = newRange & 0xFF; return new MethodAnnotation( parallelStreamMethod || other.parallelStreamMethod, missingFromLatestAndroidJar || other.missingFromLatestAndroidJar,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedMethodsGenerator.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClassesGenerator.java similarity index 84% rename from src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedMethodsGenerator.java rename to src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClassesGenerator.java index 752262f..397c5f6 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedMethodsGenerator.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClassesGenerator.java
@@ -15,12 +15,14 @@ import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.DexApplication; import com.android.tools.r8.graph.DexClass; +import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.DirectMappedDexApplication; +import com.android.tools.r8.graph.FieldResolutionResult; import com.android.tools.r8.graph.MethodAccessFlags; import com.android.tools.r8.graph.MethodResolutionResult; import com.android.tools.r8.ir.desugar.BackportedMethodRewriter; @@ -28,6 +30,7 @@ import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification; import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser; import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.ClassAnnotation; +import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.FieldAnnotation; import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.MethodAnnotation; import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification; import com.android.tools.r8.shaking.MainDexInfo; @@ -47,13 +50,13 @@ import java.util.Set; import java.util.concurrent.ExecutorService; -public class SupportedMethodsGenerator { +public class SupportedClassesGenerator { private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar"; private final InternalOptions options; - public SupportedMethodsGenerator(InternalOptions options) { + public SupportedClassesGenerator(InternalOptions options) { this.options = options; } @@ -61,7 +64,7 @@ throws IOException { SupportedClasses.Builder builder = SupportedClasses.builder(); // First analyze everything which is supported when desugaring for api 1. - collectSupportedMethodsInB(desugaredLibraryImplementation, specification, builder); + collectSupportedMembersInB(desugaredLibraryImplementation, specification, builder); // Second annotate all apis which are partially and/or fully supported. AndroidApp library = AndroidApp.builder() @@ -71,7 +74,7 @@ new ApplicationReader(library, options, Timing.empty()).read().toDirect(); annotateMethodsNotOnLatestAndroidJar(appForMax, builder); annotateParallelMethods(builder); - annotatePartialDesugaringMethods(builder, specification); + annotatePartialDesugaringMembers(builder, specification); annotateClasses(builder, appForMax); return builder.build(); } @@ -81,6 +84,10 @@ builder.forEachClassAndMethods( (clazz, methods) -> { + ClassAnnotation classAnnotation = builder.getClassAnnotation(clazz.type); + if (classAnnotation != null && classAnnotation.isAdditionalMembersOnClass()) { + return; + } DexClass maxClass = appForMax.definitionFor(clazz.type); List<DexMethod> missing = new ArrayList<>(); boolean fullySupported = true; @@ -94,6 +101,8 @@ missing.add(method.getReference()); fullySupported = false; } + } + for (DexEncodedMethod method : clazz.methods()) { MethodAnnotation methodAnnotation = builder.getMethodAnnotation(method.getReference()); if (methodAnnotation != null && !methodAnnotation.isCovariantReturnSupported()) { fullySupported = false; @@ -103,7 +112,7 @@ }); } - private void annotatePartialDesugaringMethods( + private void annotatePartialDesugaringMembers( SupportedClasses.Builder builder, Path specification) throws IOException { for (int api = AndroidApiLevel.K.getLevel(); api <= MAX_TESTED_ANDROID_API_LEVEL.getLevel(); @@ -149,8 +158,7 @@ if (machineSpecification.getEmulatedInterfaces().containsKey(dexMethod.getHolderType()) && encodedMethod.isStatic()) { // Static methods on emulated interfaces are always supported if the emulated - // interface is - // supported. + // interface is supported. return; } MethodResolutionResult methodResolutionResult = @@ -163,6 +171,23 @@ builder.annotateMethod(dexMethod, MethodAnnotation.createMissingInMinApi(finalApi)); } }); + + builder.forEachClassAndField( + (clazz, encodedField) -> { + if (machineSpecification.isContextTypeMaintainedOrRewritten( + encodedField.getHolderType()) + || machineSpecification + .getStaticFieldRetarget() + .containsKey(encodedField.getReference())) { + return; + } + FieldResolutionResult fieldResolutionResult = + appInfo.resolveField(encodedField.getReference()); + if (fieldResolutionResult.isFailedResolution()) { + builder.annotateField( + encodedField.getReference(), FieldAnnotation.createMissingInMinApi(finalApi)); + } + }); } } @@ -193,7 +218,7 @@ return Paths.get(jar); } - private void collectSupportedMethodsInB( + private void collectSupportedMembersInB( Collection<Path> desugaredLibraryImplementation, Path specification, SupportedClasses.Builder builder) @@ -252,6 +277,7 @@ builder.addSupportedMethod(clazz, method); } addBackports(clazz, backports, builder, amendedAppForMax); + builder.annotateClass(clazz.type, ClassAnnotation.getAdditionnalMembersOnClass()); } else { // All methods in maintained or rewritten classes are supported. if ((clazz.accessFlags.isPublic() || clazz.accessFlags.isProtected()) @@ -264,6 +290,12 @@ builder.addSupportedMethod(clazz, method); } addBackports(clazz, backports, builder, amendedAppForMax); + for (DexEncodedField field : clazz.fields()) { + if (!field.isPublic() && !field.isProtected()) { + continue; + } + builder.addSupportedField(clazz, field); + } } } } @@ -276,6 +308,7 @@ DexEncodedMethod dexEncodedMethod = dexClass.lookupMethod(method); if (dexEncodedMethod != null) { builder.addSupportedMethod(dexClass, dexEncodedMethod); + builder.annotateClass(dexClass.type, ClassAnnotation.getAdditionnalMembersOnClass()); return; } } @@ -283,7 +316,29 @@ DexEncodedMethod dexEncodedMethod = dexClass.lookupMethod(method); assert dexEncodedMethod != null; builder.addSupportedMethod(dexClass, dexEncodedMethod); + builder.annotateClass(dexClass.type, ClassAnnotation.getAdditionnalMembersOnClass()); }); + + machineSpecification + .getStaticFieldRetarget() + .forEach( + (field, rewritten) -> { + DexClass dexClass = implementationApplication.definitionFor(field.getHolderType()); + if (dexClass != null) { + DexEncodedField dexEncodedField = dexClass.lookupField(field); + if (dexEncodedField != null) { + builder.addSupportedField(dexClass, dexEncodedField); + builder.annotateClass( + dexClass.type, ClassAnnotation.getAdditionnalMembersOnClass()); + return; + } + } + dexClass = amendedAppForMax.definitionFor(field.getHolderType()); + DexEncodedField dexEncodedField = dexClass.lookupField(field); + assert dexEncodedField != null; + builder.addSupportedField(dexClass, dexEncodedField); + builder.annotateClass(dexClass.type, ClassAnnotation.getAdditionnalMembersOnClass()); + }); } private void addBackports(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java index cc5abfd..dcf5c0a 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
@@ -138,7 +138,7 @@ DexEncodedMethod newMethod = createForwardingMethod(itfMethod, descriptor, clazz); clazz.addVirtualMethod(newMethod); eventConsumer.acceptDesugaredLibraryRetargeterForwardingMethod( - new ProgramMethod(clazz, newMethod)); + new ProgramMethod(clazz, newMethod), descriptor); } } }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSynthesizerEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSynthesizerEventConsumer.java index 63dcba2..6e491ff 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSynthesizerEventConsumer.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSynthesizerEventConsumer.java
@@ -8,6 +8,7 @@ import com.android.tools.r8.graph.DexClasspathClass; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor; public interface DesugaredLibraryRetargeterSynthesizerEventConsumer { @@ -18,13 +19,14 @@ interface DesugaredLibraryRetargeterInstructionEventConsumer { void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz); - void acceptCovariantRetargetMethod(ProgramMethod method); + void acceptCovariantRetargetMethod(ProgramMethod method, ProgramMethod context); } interface DesugaredLibraryRetargeterPostProcessingEventConsumer extends DesugaredLibraryRetargeterInstructionEventConsumer { void acceptInterfaceInjection(DexProgramClass clazz, DexClass newInterface); - void acceptDesugaredLibraryRetargeterForwardingMethod(ProgramMethod method); + void acceptDesugaredLibraryRetargeterForwardingMethod( + ProgramMethod method, EmulatedDispatchMethodDescriptor descriptor); } }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java index ae5814a..52fa933 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java
@@ -57,7 +57,7 @@ .setNonStaticSource(target) .setCastResult() .build())); - eventConsumer.acceptCovariantRetargetMethod(method); + eventConsumer.acceptCovariantRetargetMethod(method, methodProcessingContext.getMethodContext()); return method.getReference(); }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java index 32e2c37..6a74cf3 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -30,6 +30,8 @@ import com.android.tools.r8.graph.LookupMethodTarget; import com.android.tools.r8.graph.MethodAccessFlags; import com.android.tools.r8.graph.MethodResolutionResult; +import com.android.tools.r8.graph.MethodResolutionResult.FailedResolutionResult; +import com.android.tools.r8.graph.MethodResolutionResult.NoSuchMethodResult; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod; import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode; @@ -126,16 +128,23 @@ private static class SyntheticThrowingMethodInfo extends SyntheticMethodInfo { private final DexType errorType; + private final FailedResolutionResult resolutionResult; - SyntheticThrowingMethodInfo(ProgramMethod method, DexType errorType) { + SyntheticThrowingMethodInfo( + ProgramMethod method, DexType errorType, FailedResolutionResult resolutionResult) { super(method); this.errorType = errorType; + this.resolutionResult = resolutionResult; } DexType getErrorType() { return errorType; } + FailedResolutionResult getResolutionResult() { + return resolutionResult; + } + @Override SyntheticThrowingMethodInfo asThrowingMethodInfo() { return this; @@ -511,8 +520,11 @@ eventConsumer.acceptInterfaceMethodDesugaringForwardingMethod( info.getMethod(), info.asForwardingMethodInfo().getBaseMethod()); } else { + SyntheticThrowingMethodInfo throwingMethodInfo = info.asThrowingMethodInfo(); eventConsumer.acceptThrowingMethod( - info.getMethod(), info.asThrowingMethodInfo().getErrorType()); + info.getMethod(), + throwingMethodInfo.getErrorType(), + throwingMethodInfo.getResolutionResult()); } } }, @@ -842,15 +854,15 @@ } if (resolutionResult.isFailedResolution()) { if (resolutionResult.isIncompatibleClassChangeErrorResult()) { - addICCEThrowingMethod(method, clazz); + addICCEThrowingMethod(method, clazz, resolutionResult.asFailedResolution()); return; } if (resolutionResult.isNoSuchMethodErrorResult(clazz, appInfo)) { - addNoSuchMethodErrorThrowingMethod(method, clazz); + addNoSuchMethodErrorThrowingMethod(method, clazz, resolutionResult.asFailedResolution()); return; } assert resolutionResult.isIllegalAccessErrorResult(clazz, appInfo); - addIllegalAccessErrorThrowingMethod(method, clazz); + addIllegalAccessErrorThrowingMethod(method, clazz, resolutionResult.asFailedResolution()); return; } } @@ -878,7 +890,8 @@ + "). Please report this issue in the D8/R8 bug tracker at" + " https://issuetracker.google.com/issues/237507594."); // To be able to resume compilation we add a NoSuchMethodErrorThrowingMethod. - addNoSuchMethodErrorThrowingMethod(method, clazz); + addNoSuchMethodErrorThrowingMethod( + method, clazz, NoSuchMethodResult.getEmptyNoSuchMethodResult()); return; } DexClassAndMethod virtualDispatchTarget = lookupMethodTarget.getTarget(); @@ -919,27 +932,37 @@ assert existingMethodInfo == null; } - private void addSyntheticThrowingMethod(ProgramMethod method, DexType errorType) { + private void addSyntheticThrowingMethod( + ProgramMethod method, DexType errorType, FailedResolutionResult resolutionResult) { SyntheticMethodInfo existingMethodInfo = newSyntheticMethods .computeIfAbsent(method.getHolder(), key -> new ConcurrentHashMap<>()) - .put(method.getReference(), new SyntheticThrowingMethodInfo(method, errorType)); + .put( + method.getReference(), + new SyntheticThrowingMethodInfo(method, errorType, resolutionResult)); assert existingMethodInfo == null; } - private void addICCEThrowingMethod(DexMethod method, DexClass clazz) { - addThrowingMethod(method, clazz, dexItemFactory.icceType); + private void addICCEThrowingMethod( + DexMethod method, DexClass clazz, FailedResolutionResult resolutionResult) { + addThrowingMethod(method, clazz, dexItemFactory.icceType, resolutionResult); } - private void addIllegalAccessErrorThrowingMethod(DexMethod method, DexClass clazz) { - addThrowingMethod(method, clazz, dexItemFactory.illegalAccessErrorType); + private void addIllegalAccessErrorThrowingMethod( + DexMethod method, DexClass clazz, FailedResolutionResult resolutionResult) { + addThrowingMethod(method, clazz, dexItemFactory.illegalAccessErrorType, resolutionResult); } - private void addNoSuchMethodErrorThrowingMethod(DexMethod method, DexClass clazz) { - addThrowingMethod(method, clazz, dexItemFactory.noSuchMethodErrorType); + private void addNoSuchMethodErrorThrowingMethod( + DexMethod method, DexClass clazz, FailedResolutionResult resolutionResult) { + addThrowingMethod(method, clazz, dexItemFactory.noSuchMethodErrorType, resolutionResult); } - private void addThrowingMethod(DexMethod method, DexClass clazz, DexType errorType) { + private void addThrowingMethod( + DexMethod method, + DexClass clazz, + DexType errorType, + FailedResolutionResult resolutionResult) { if (!clazz.isProgramClass()) { return; } @@ -953,7 +976,8 @@ createExceptionThrowingCfCode(newMethod, accessFlags, errorType, dexItemFactory)) .disableAndroidApiLevelCheck() .build(); - addSyntheticThrowingMethod(newEncodedMethod.asProgramMethod(clazz.asProgramClass()), errorType); + addSyntheticThrowingMethod( + newEncodedMethod.asProgramMethod(clazz.asProgramClass()), errorType, resolutionResult); } private static CfCode createExceptionThrowingCfCode(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java index dde1382..a918d81 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java
@@ -8,6 +8,7 @@ import com.android.tools.r8.graph.DexClasspathClass; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.MethodResolutionResult.FailedResolutionResult; import com.android.tools.r8.graph.ProgramMethod; public interface InterfaceProcessingDesugaringEventConsumer { @@ -18,7 +19,8 @@ void acceptEmulatedInterfaceMarkerInterface( DexProgramClass clazz, DexClasspathClass newInterface); - void acceptThrowingMethod(ProgramMethod method, DexType errorType); + void acceptThrowingMethod( + ProgramMethod method, DexType errorType, FailedResolutionResult resolutionResult); void warnMissingInterface( DexProgramClass context, DexType missing, InterfaceDesugaringSyntheticHelper helper);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java index 34324ff..19df173 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
@@ -19,7 +19,8 @@ import com.android.tools.r8.graph.ProgramField; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.UseRegistry; -import com.android.tools.r8.ir.conversion.MethodProcessor; +import com.android.tools.r8.ir.conversion.D8MethodProcessor; +import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingNestBasedAccessDesugaringEventConsumer; import com.android.tools.r8.utils.ThreadUtils; import com.google.common.collect.Iterables; import java.util.ArrayList; @@ -81,7 +82,8 @@ } public void synthesizeBridgesForNestBasedAccessesOnClasspath( - MethodProcessor methodProcessor, ExecutorService executorService) throws ExecutionException { + D8MethodProcessor methodProcessor, ExecutorService executorService) + throws ExecutionException { List<DexClasspathClass> classpathClassesInNests = new ArrayList<>(); forEachNest( nest -> { @@ -92,35 +94,37 @@ }); NestBasedAccessDesugaringEventConsumer eventConsumer = - new NestBasedAccessDesugaringEventConsumer() { + ArtProfileRewritingNestBasedAccessDesugaringEventConsumer.attach( + methodProcessor.getArtProfileCollectionAdditions(), + new NestBasedAccessDesugaringEventConsumer() { - @Override - public void acceptNestConstructorBridge( - ProgramMethod target, - ProgramMethod bridge, - DexProgramClass argumentClass, - DexClassAndMethod context) { - methodProcessor.scheduleDesugaredMethodForProcessing(bridge); - } + @Override + public void acceptNestConstructorBridge( + ProgramMethod target, + ProgramMethod bridge, + DexProgramClass argumentClass, + DexClassAndMethod context) { + methodProcessor.scheduleDesugaredMethodForProcessing(bridge); + } - @Override - public void acceptNestFieldGetBridge( - ProgramField target, ProgramMethod bridge, DexClassAndMethod context) { - methodProcessor.scheduleDesugaredMethodForProcessing(bridge); - } + @Override + public void acceptNestFieldGetBridge( + ProgramField target, ProgramMethod bridge, DexClassAndMethod context) { + methodProcessor.scheduleDesugaredMethodForProcessing(bridge); + } - @Override - public void acceptNestFieldPutBridge( - ProgramField target, ProgramMethod bridge, DexClassAndMethod context) { - methodProcessor.scheduleDesugaredMethodForProcessing(bridge); - } + @Override + public void acceptNestFieldPutBridge( + ProgramField target, ProgramMethod bridge, DexClassAndMethod context) { + methodProcessor.scheduleDesugaredMethodForProcessing(bridge); + } - @Override - public void acceptNestMethodBridge( - ProgramMethod target, ProgramMethod bridge, DexClassAndMethod context) { - methodProcessor.scheduleDesugaredMethodForProcessing(bridge); - } - }; + @Override + public void acceptNestMethodBridge( + ProgramMethod target, ProgramMethod bridge, DexClassAndMethod context) { + methodProcessor.scheduleDesugaredMethodForProcessing(bridge); + } + }); ThreadUtils.processItems( classpathClassesInNests, clazz -> synthesizeBridgesForNestBasedAccessesOnClasspath(clazz, eventConsumer),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaringEventConsumer.java index dac3668..afbeedc 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaringEventConsumer.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaringEventConsumer.java
@@ -25,4 +25,48 @@ void acceptNestMethodBridge( ProgramMethod target, ProgramMethod bridge, DexClassAndMethod context); + + static EmptyNestBasedAccessDesugaringEventConsumer empty() { + return EmptyNestBasedAccessDesugaringEventConsumer.getInstance(); + } + + class EmptyNestBasedAccessDesugaringEventConsumer + implements NestBasedAccessDesugaringEventConsumer { + + private static final EmptyNestBasedAccessDesugaringEventConsumer INSTANCE = + new EmptyNestBasedAccessDesugaringEventConsumer(); + + private EmptyNestBasedAccessDesugaringEventConsumer() {} + + static EmptyNestBasedAccessDesugaringEventConsumer getInstance() { + return INSTANCE; + } + + @Override + public void acceptNestConstructorBridge( + ProgramMethod target, + ProgramMethod bridge, + DexProgramClass argumentClass, + DexClassAndMethod context) { + // Intentionally empty. + } + + @Override + public void acceptNestFieldGetBridge( + ProgramField target, ProgramMethod bridge, DexClassAndMethod context) { + // Intentionally empty. + } + + @Override + public void acceptNestFieldPutBridge( + ProgramField target, ProgramMethod bridge, DexClassAndMethod context) { + // Intentionally empty. + } + + @Override + public void acceptNestMethodBridge( + ProgramMethod target, ProgramMethod bridge, DexClassAndMethod context) { + // Intentionally empty. + } + } }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java index d8382b7..ceb6b75 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java
@@ -22,6 +22,7 @@ import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexProto; import com.android.tools.r8.graph.DexString; import com.android.tools.r8.graph.DexType; @@ -174,19 +175,24 @@ } private void ensureMethodHandlesLookupClass( - VarHandleDesugaringEventConsumer eventConsumer, Collection<ProgramDefinition> contexts) { - appView - .getSyntheticItems() - .ensureGlobalClass( - () -> new MissingGlobalSyntheticsConsumerDiagnostic("VarHandle desugaring"), - kinds -> kinds.METHOD_HANDLES_LOOKUP, - factory.lookupType, - contexts, - appView, - builder -> - VarHandleDesugaringMethods.generateDesugarMethodHandlesLookupClass( - builder, appView.dexItemFactory()), - eventConsumer::acceptVarHandleDesugaringClass); + VarHandleDesugaringEventConsumer eventConsumer, + Collection<? extends ProgramDefinition> contexts) { + DexProgramClass clazz = + appView + .getSyntheticItems() + .ensureGlobalClass( + () -> new MissingGlobalSyntheticsConsumerDiagnostic("VarHandle desugaring"), + kinds -> kinds.METHOD_HANDLES_LOOKUP, + factory.lookupType, + contexts, + appView, + builder -> + VarHandleDesugaringMethods.generateDesugarMethodHandlesLookupClass( + builder, appView.dexItemFactory()), + eventConsumer::acceptVarHandleDesugaringClass); + for (ProgramDefinition context : contexts) { + eventConsumer.acceptVarHandleDesugaringClassContext(clazz, context); + } } private void ensureMethodHandlesLookupClass( @@ -195,19 +201,24 @@ } private void ensureVarHandleClass( - VarHandleDesugaringEventConsumer eventConsumer, Collection<ProgramDefinition> contexts) { - appView - .getSyntheticItems() - .ensureGlobalClass( - () -> new MissingGlobalSyntheticsConsumerDiagnostic("VarHandle desugaring"), - kinds -> kinds.VAR_HANDLE, - factory.varHandleType, - contexts, - appView, - builder -> - VarHandleDesugaringMethods.generateDesugarVarHandleClass( - builder, appView.dexItemFactory()), - eventConsumer::acceptVarHandleDesugaringClass); + VarHandleDesugaringEventConsumer eventConsumer, + Collection<? extends ProgramDefinition> contexts) { + DexProgramClass clazz = + appView + .getSyntheticItems() + .ensureGlobalClass( + () -> new MissingGlobalSyntheticsConsumerDiagnostic("VarHandle desugaring"), + kinds -> kinds.VAR_HANDLE, + factory.varHandleType, + contexts, + appView, + builder -> + VarHandleDesugaringMethods.generateDesugarVarHandleClass( + builder, appView.dexItemFactory()), + eventConsumer::acceptVarHandleDesugaringClass); + for (ProgramDefinition context : contexts) { + eventConsumer.acceptVarHandleDesugaringClassContext(clazz, context); + } } private void ensureVarHandleClass( @@ -598,9 +609,9 @@ DexApplicationReadFlags flags, Predicate<DexApplicationReadFlags> hasReadReferenceFromProgramClass, Function<DexApplicationReadFlags, Set<DexType>> getWitnesses, - Consumer<List<ProgramDefinition>> consumeProgramWitnesses) { + Consumer<? super List<DexProgramClass>> consumeProgramWitnesses) { if (hasReadReferenceFromProgramClass.test(flags)) { - List<ProgramDefinition> classes = new ArrayList<>(); + List<DexProgramClass> classes = new ArrayList<>(); for (DexType witness : getWitnesses.apply(flags)) { DexClass dexClass = appView.contextIndependentDefinitionFor(witness); assert dexClass != null;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringEventConsumer.java index 8251809..d27e4d8 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringEventConsumer.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringEventConsumer.java
@@ -4,8 +4,11 @@ package com.android.tools.r8.ir.desugar.varhandle; import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.ProgramDefinition; public interface VarHandleDesugaringEventConsumer { - void acceptVarHandleDesugaringClass(DexProgramClass varHandleClass); + void acceptVarHandleDesugaringClass(DexProgramClass clazz); + + void acceptVarHandleDesugaringClassContext(DexProgramClass clazz, ProgramDefinition context); }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssertionErrorTwoArgsConstructorRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssertionErrorTwoArgsConstructorRewriter.java index 6d5083c..e464f9c 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/AssertionErrorTwoArgsConstructorRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/AssertionErrorTwoArgsConstructorRewriter.java
@@ -21,6 +21,7 @@ import com.android.tools.r8.ir.code.InvokeStatic; import com.android.tools.r8.ir.code.NewInstance; import com.android.tools.r8.ir.code.Value; +import com.android.tools.r8.ir.conversion.MethodProcessor; import com.android.tools.r8.ir.desugar.backports.BackportedMethods; import com.android.tools.r8.ir.optimize.info.OptimizationFeedback; import com.android.tools.r8.utils.InternalOptions; @@ -42,7 +43,10 @@ this.dexItemFactory = appView.dexItemFactory(); } - public void rewrite(IRCode code, MethodProcessingContext methodProcessingContext) { + public void rewrite( + IRCode code, + MethodProcessor methodProcessor, + MethodProcessingContext methodProcessingContext) { if (options.canUseAssertionErrorTwoArgumentConstructor()) { return; } @@ -66,7 +70,8 @@ } InvokeStatic invoke = InvokeStatic.builder() - .setMethod(createSynthetic(methodProcessingContext).getReference()) + .setMethod( + createSynthetic(methodProcessor, methodProcessingContext).getReference()) .setFreshOutValue( code, dexItemFactory.assertionErrorType.toTypeElement(appView)) .setPosition(current) @@ -94,7 +99,8 @@ return synthesizedMethods; } - private ProgramMethod createSynthetic(MethodProcessingContext methodProcessingContext) { + private ProgramMethod createSynthetic( + MethodProcessor methodProcessor, MethodProcessingContext methodProcessingContext) { DexItemFactory factory = appView.dexItemFactory(); DexProto proto = factory.createProto(factory.assertionErrorType, factory.stringType, factory.throwableType); @@ -126,6 +132,9 @@ .toTypeElement(appView, Nullability.definitelyNotNull()) .asClassType())); } + methodProcessor + .getEventConsumer() + .acceptAssertionErrorCreateMethod(method, methodProcessingContext.getMethodContext()); return method; } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssertionErrorTwoArgsConstructorRewriterEventConsumer.java b/src/main/java/com/android/tools/r8/ir/optimize/AssertionErrorTwoArgsConstructorRewriterEventConsumer.java new file mode 100644 index 0000000..8b15685 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/optimize/AssertionErrorTwoArgsConstructorRewriterEventConsumer.java
@@ -0,0 +1,12 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir.optimize; + +import com.android.tools.r8.graph.ProgramMethod; + +public interface AssertionErrorTwoArgsConstructorRewriterEventConsumer { + + void acceptAssertionErrorCreateMethod(ProgramMethod method, ProgramMethod context); +}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java index 12030bf..534b5a9 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
@@ -22,6 +22,7 @@ import com.android.tools.r8.ir.code.InvokeStatic; import com.android.tools.r8.ir.code.InvokeVirtual; import com.android.tools.r8.ir.code.Value; +import com.android.tools.r8.ir.conversion.MethodProcessor; import com.android.tools.r8.ir.desugar.ServiceLoaderSourceCode; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.BooleanBox; @@ -75,7 +76,10 @@ return serviceLoadMethods; } - public void rewrite(IRCode code, MethodProcessingContext methodProcessingContext) { + public void rewrite( + IRCode code, + MethodProcessor methodProcessor, + MethodProcessingContext methodProcessingContext) { DexItemFactory factory = appView.dexItemFactory(); InstructionListIterator instructionIterator = code.instructionListIterator(); // Create a map from service type to loader methods local to this context since two @@ -175,7 +179,8 @@ constClass.getValue(), service -> { DexEncodedMethod addedMethod = - createSynthesizedMethod(service, classes, methodProcessingContext); + createSynthesizedMethod( + service, classes, methodProcessor, methodProcessingContext); if (appView.options().isGeneratingClassFiles()) { addedMethod.upgradeClassFileVersion( code.context().getDefinition().getClassFileVersion()); @@ -191,6 +196,7 @@ private DexEncodedMethod createSynthesizedMethod( DexType serviceType, List<DexClass> classes, + MethodProcessor methodProcessor, MethodProcessingContext methodProcessingContext) { DexProto proto = appView.dexItemFactory().createProto(appView.dexItemFactory().iteratorType); ProgramMethod method = @@ -215,6 +221,9 @@ synchronized (serviceLoadMethods) { serviceLoadMethods.add(method); } + methodProcessor + .getEventConsumer() + .acceptServiceLoaderLoadUtilityMethod(method, methodProcessingContext.getMethodContext()); return method.getDefinition(); }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriterEventConsumer.java b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriterEventConsumer.java new file mode 100644 index 0000000..379021b --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriterEventConsumer.java
@@ -0,0 +1,12 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir.optimize; + +import com.android.tools.r8.graph.ProgramMethod; + +public interface ServiceLoaderRewriterEventConsumer { + + void acceptServiceLoaderLoadUtilityMethod(ProgramMethod method, ProgramMethod context); +}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java index d3f9395..a76e4d9 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
@@ -34,7 +34,7 @@ import java.util.Map; import java.util.Set; -class EnumUnboxingLens extends NestedGraphLens { +public class EnumUnboxingLens extends NestedGraphLens { private final AbstractValueFactory abstractValueFactory; private final Map<DexMethod, RewrittenPrototypeDescription> prototypeChangesPerMethod; @@ -64,6 +64,11 @@ } @Override + public EnumUnboxingLens asEnumUnboxerLens() { + return this; + } + + @Override protected RewrittenPrototypeDescription internalDescribePrototypeChanges( RewrittenPrototypeDescription prototypeChanges, DexMethod method) { // Rewrite the single value of the given RewrittenPrototypeDescription if it is referring to an
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineOptimizationEventConsumer.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineOptimizationEventConsumer.java index 4d8a9ed..86ac8ab 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineOptimizationEventConsumer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineOptimizationEventConsumer.java
@@ -4,32 +4,20 @@ package com.android.tools.r8.ir.optimize.outliner; +import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.ProgramMethod; -import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions; -import com.android.tools.r8.profile.art.rewriting.ConcreteArtProfileCollectionAdditions; +import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingOutlineOptimizationEventConsumer; +import com.android.tools.r8.shaking.AppInfoWithLiveness; import java.util.Collection; public interface OutlineOptimizationEventConsumer { void acceptOutlineMethod(ProgramMethod method, Collection<ProgramMethod> contexts); - static OutlineOptimizationEventConsumer create( - ArtProfileCollectionAdditions collectionAdditions) { - if (collectionAdditions.isNop()) { - return empty(); - } - return create(collectionAdditions.asConcrete()); - } + void finished(AppView<AppInfoWithLiveness> appView); - static OutlineOptimizationEventConsumer create( - ConcreteArtProfileCollectionAdditions collectionAdditions) { - return (method, contexts) -> { - for (ProgramMethod context : contexts) { - collectionAdditions.applyIfContextIsInProfile( - context, - additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder())); - } - }; + static OutlineOptimizationEventConsumer create(AppView<AppInfoWithLiveness> appView) { + return ArtProfileRewritingOutlineOptimizationEventConsumer.attach(appView, empty()); } static EmptyOutlineOptimizationEventConsumer empty() { @@ -51,5 +39,10 @@ public void acceptOutlineMethod(ProgramMethod method, Collection<ProgramMethod> contexts) { // Intentionally empty. } + + @Override + public void finished(AppView<AppInfoWithLiveness> appView) { + // Intentionally empty. + } } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java index 7382a1e..b56f246 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java
@@ -68,7 +68,6 @@ import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed; import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore; import com.android.tools.r8.origin.Origin; -import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.InternalOptions.OutlineOptions; @@ -1342,6 +1341,8 @@ timing.begin("IR conversion phase 3"); ProgramMethodSet methodsSelectedForOutlining = selectMethodsForOutlining(); if (!methodsSelectedForOutlining.isEmpty()) { + OutlineOptimizationEventConsumer eventConsumer = + OutlineOptimizationEventConsumer.create(appView); forEachSelectedOutliningMethod( converter, methodsSelectedForOutlining, @@ -1350,14 +1351,11 @@ identifyOutlineSites(code); }, executorService); - ArtProfileCollectionAdditions artProfileCollectionAdditions = - ArtProfileCollectionAdditions.create(appView); - List<ProgramMethod> outlineMethods = - buildOutlineMethods( - OutlineOptimizationEventConsumer.create(artProfileCollectionAdditions)); - artProfileCollectionAdditions.commit(appView); - MethodProcessorEventConsumer eventConsumer = MethodProcessorEventConsumer.empty(); - converter.optimizeSynthesizedMethods(outlineMethods, eventConsumer, executorService); + List<ProgramMethod> outlineMethods = buildOutlineMethods(eventConsumer); + MethodProcessorEventConsumer methodProcessorEventConsumer = + MethodProcessorEventConsumer.empty(); + converter.optimizeSynthesizedMethods( + outlineMethods, methodProcessorEventConsumer, executorService); feedback.updateVisibleOptimizationInfo(); forEachSelectedOutliningMethod( converter, @@ -1374,6 +1372,7 @@ feedback.updateVisibleOptimizationInfo(); assert checkAllOutlineSitesFoundAgain(); outlineMethods.forEach(m -> m.getDefinition().markNotProcessed()); + eventConsumer.finished(appView); } timing.end(); }
diff --git a/src/main/java/com/android/tools/r8/lightir/ByteWriter.java b/src/main/java/com/android/tools/r8/lightir/ByteWriter.java index 0a2a75e..30b5355 100644 --- a/src/main/java/com/android/tools/r8/lightir/ByteWriter.java +++ b/src/main/java/com/android/tools/r8/lightir/ByteWriter.java
@@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.lightir; -/** Most primitive interface for providing consumer to the LIRWriter. */ +/** Most primitive interface for providing consumer to the {@link LirWriter}. */ public interface ByteWriter { /** Put a byte value, must represent an unsigned byte (int between 0 and 255). */
diff --git a/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java b/src/main/java/com/android/tools/r8/lightir/IR2LirConverter.java similarity index 93% rename from src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java rename to src/main/java/com/android/tools/r8/lightir/IR2LirConverter.java index b1ad627..fbd5571 100644 --- a/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java +++ b/src/main/java/com/android/tools/r8/lightir/IR2LirConverter.java
@@ -12,7 +12,7 @@ import com.android.tools.r8.ir.code.InstructionIterator; import com.android.tools.r8.ir.code.Phi; import com.android.tools.r8.ir.code.Value; -import com.android.tools.r8.lightir.LIRBuilder.BlockIndexGetter; +import com.android.tools.r8.lightir.LirBuilder.BlockIndexGetter; import com.android.tools.r8.utils.ListUtils; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; @@ -25,25 +25,25 @@ import java.util.Comparator; import java.util.List; -public class IR2LIRConverter { +public class IR2LirConverter { private final DexItemFactory factory; private final IRCode irCode; private final Reference2IntMap<BasicBlock> blocks = new Reference2IntOpenHashMap<>(); private final Reference2IntMap<Value> values = new Reference2IntOpenHashMap<>(); - private final LIRBuilder<Value, BasicBlock> builder; + private final LirBuilder<Value, BasicBlock> builder; - private IR2LIRConverter(DexItemFactory factory, IRCode irCode) { + private IR2LirConverter(DexItemFactory factory, IRCode irCode) { this.factory = factory; this.irCode = irCode; this.builder = - new LIRBuilder<Value, BasicBlock>( + new LirBuilder<Value, BasicBlock>( irCode.context().getReference(), values::getInt, blocks::getInt, factory) .setMetadata(irCode.metadata()); } - public static LIRCode translate(IRCode irCode, DexItemFactory factory) { - return new IR2LIRConverter(factory, irCode).internalTranslate(); + public static LirCode translate(IRCode irCode, DexItemFactory factory) { + return new IR2LirConverter(factory, irCode).internalTranslate(); } private void recordBlock(BasicBlock block, int blockIndex) { @@ -57,7 +57,7 @@ } } - private LIRCode internalTranslate() { + private LirCode internalTranslate() { irCode.traceBlocks(); computeBlockAndValueTables(); computeInstructions(); @@ -108,7 +108,7 @@ continue; } } - instruction.buildLIR(builder); + instruction.buildLir(builder); currentValueIndex++; } assert builder.verifyCurrentValueIndex(currentValueIndex);
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRUtils.java b/src/main/java/com/android/tools/r8/lightir/LIRUtils.java deleted file mode 100644 index 09dcd81..0000000 --- a/src/main/java/com/android/tools/r8/lightir/LIRUtils.java +++ /dev/null
@@ -1,17 +0,0 @@ -// Copyright (c) 2022, 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.lightir; - -public class LIRUtils { - - private LIRUtils() {} - - public static int encodeValueIndex(int absoluteValueIndex, int referencingValueContext) { - return referencingValueContext - absoluteValueIndex; - } - - public static int decodeValueIndex(int encodedValueIndex, int referencingValueContext) { - return referencingValueContext - encodedValueIndex; - } -}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java similarity index 97% rename from src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java rename to src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java index 56fb836..d0677f4 100644 --- a/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java +++ b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
@@ -38,7 +38,7 @@ import com.android.tools.r8.ir.code.StaticGet; import com.android.tools.r8.ir.code.Value; import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions; -import com.android.tools.r8.lightir.LIRCode.PositionEntry; +import com.android.tools.r8.lightir.LirCode.PositionEntry; import com.android.tools.r8.utils.ListUtils; import it.unimi.dsi.fastutil.ints.Int2ReferenceMap; import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap; @@ -48,11 +48,11 @@ import java.util.LinkedList; import java.util.List; -public class LIR2IRConverter { +public class Lir2IRConverter { - private LIR2IRConverter() {} + private Lir2IRConverter() {} - public static IRCode translate(ProgramMethod method, LIRCode lirCode, AppView<?> appView) { + public static IRCode translate(ProgramMethod method, LirCode lirCode, AppView<?> appView) { Parser parser = new Parser(lirCode, method.getReference(), appView); parser.parseArguments(method); lirCode.forEach(view -> view.accept(parser)); @@ -63,12 +63,12 @@ * When building IR the structured LIR parser is used to obtain the decoded operand indexes. The * below parser subclass handles translation of indexes to SSA values. */ - private static class Parser extends LIRParsedInstructionCallback { + private static class Parser extends LirParsedInstructionCallback { private static final int ENTRY_BLOCK_INDEX = -1; private final AppView<?> appView; - private final LIRCode code; + private final LirCode code; private final NumberGenerator valueNumberGenerator = new NumberGenerator(); private final NumberGenerator basicBlockNumberGenerator = new NumberGenerator(); @@ -82,7 +82,7 @@ private PositionEntry nextPositionEntry = null; private int nextIndexInPositionsTable = 0; - public Parser(LIRCode code, DexMethod method, AppView<?> appView) { + public Parser(LirCode code, DexMethod method, AppView<?> appView) { super(code); assert code.getPositionTable().length > 0; assert code.getPositionTable()[0].fromInstructionIndex == 0;
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java similarity index 75% rename from src/main/java/com/android/tools/r8/lightir/LIRBuilder.java rename to src/main/java/com/android/tools/r8/lightir/LirBuilder.java index 9095c8b..7ec7244 100644 --- a/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java +++ b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
@@ -20,9 +20,9 @@ import com.android.tools.r8.ir.code.Position; import com.android.tools.r8.ir.code.Position.SyntheticPosition; import com.android.tools.r8.ir.code.ValueType; -import com.android.tools.r8.lightir.LIRCode.DebugLocalInfoTable; -import com.android.tools.r8.lightir.LIRCode.PositionEntry; -import com.android.tools.r8.lightir.LIRCode.TryCatchTable; +import com.android.tools.r8.lightir.LirCode.DebugLocalInfoTable; +import com.android.tools.r8.lightir.LirCode.PositionEntry; +import com.android.tools.r8.lightir.LirCode.TryCatchTable; import com.android.tools.r8.utils.ListUtils; import com.google.common.collect.ImmutableList; import it.unimi.dsi.fastutil.ints.Int2ReferenceMap; @@ -43,7 +43,7 @@ * @param <B> Type of basic blocks. This is abstract to ensure that basic block internals are not * used in building. */ -public class LIRBuilder<V, B> { +public class LirBuilder<V, B> { // Abstraction for the only accessible properties of an SSA value. public interface ValueIndexGetter<V> { @@ -57,7 +57,7 @@ private final DexItemFactory factory; private final ByteArrayWriter byteWriter = new ByteArrayWriter(); - private final LIRWriter writer = new LIRWriter(byteWriter); + private final LirWriter writer = new LirWriter(byteWriter); private final Reference2IntMap<DexItem> constants; private final ValueIndexGetter<V> valueIndexGetter; private final BlockIndexGetter<B> blockIndexGetter; @@ -65,6 +65,7 @@ private int argumentCount = 0; private int instructionCount = 0; private IRMetadata metadata = null; + private final LirSsaValueStrategy ssaValueStrategy = LirSsaValueStrategy.get(); private Position currentPosition; private Position flushedPosition; @@ -82,7 +83,7 @@ private static final int MAX_VALUE_COUNT = 10; private int[] valueIndexBuffer = new int[MAX_VALUE_COUNT]; - public LIRBuilder( + public LirBuilder( DexMethod method, ValueIndexGetter<V> valueIndexGetter, BlockIndexGetter<B> blockIndexGetter, @@ -115,7 +116,7 @@ tryCatchRanges.put(blockIndex, handlers); } - public LIRBuilder<V, B> setCurrentPosition(Position position) { + public LirBuilder<V, B> setCurrentPosition(Position position) { assert position != null; assert position != Position.none(); currentPosition = position; @@ -150,13 +151,13 @@ private int valueIndexSize(int valueIndex, int referencingInstructionIndex) { int referencingValueIndex = referencingInstructionIndex + argumentCount; - int encodedValueIndex = LIRUtils.encodeValueIndex(valueIndex, referencingValueIndex); + int encodedValueIndex = ssaValueStrategy.encodeValueIndex(valueIndex, referencingValueIndex); return ByteUtils.intEncodingSize(encodedValueIndex); } private void writeValueIndex(int valueIndex, int referencingInstructionIndex) { int referencingValueIndex = referencingInstructionIndex + argumentCount; - int encodedValueIndex = LIRUtils.encodeValueIndex(valueIndex, referencingValueIndex); + int encodedValueIndex = ssaValueStrategy.encodeValueIndex(valueIndex, referencingValueIndex); ByteUtils.writeEncodedInt(encodedValueIndex, writer::writeOperand); } @@ -172,18 +173,18 @@ ByteUtils.writeEncodedInt(index, writer::writeOperand); } - public LIRBuilder<V, B> setMetadata(IRMetadata metadata) { + public LirBuilder<V, B> setMetadata(IRMetadata metadata) { this.metadata = metadata; return this; } - public LIRBuilder<V, B> setDebugValue(DebugLocalInfo debugInfo, int valueIndex) { + public LirBuilder<V, B> setDebugValue(DebugLocalInfo debugInfo, int valueIndex) { DebugLocalInfo old = debugLocals.put(valueIndex, debugInfo); assert old == null; return this; } - public LIRBuilder<V, B> setDebugLocalEnds(int instructionValueIndex, Set<V> endValues) { + public LirBuilder<V, B> setDebugLocalEnds(int instructionValueIndex, Set<V> endValues) { int size = endValues.size(); int[] indices = new int[size]; Iterator<V> iterator = endValues.iterator(); @@ -194,7 +195,7 @@ return this; } - public LIRBuilder<V, B> addArgument(int index, boolean knownToBeBoolean) { + public LirBuilder<V, B> addArgument(int index, boolean knownToBeBoolean) { // Arguments are implicitly given by method descriptor and not an actual instruction. assert argumentCount == index; argumentCount++; @@ -209,22 +210,22 @@ return instructionCount++; } - private LIRBuilder<V, B> addNoOperandInstruction(int opcode) { + private LirBuilder<V, B> addNoOperandInstruction(int opcode) { advanceInstructionState(); writer.writeOneByteInstruction(opcode); return this; } - private LIRBuilder<V, B> addOneItemInstruction(int opcode, DexItem item) { + private LirBuilder<V, B> addOneItemInstruction(int opcode, DexItem item) { return addInstructionTemplate(opcode, Collections.singletonList(item), Collections.emptyList()); } - private LIRBuilder<V, B> addOneValueInstruction(int opcode, V value) { + private LirBuilder<V, B> addOneValueInstruction(int opcode, V value) { return addInstructionTemplate( opcode, Collections.emptyList(), Collections.singletonList(value)); } - private LIRBuilder<V, B> addInstructionTemplate(int opcode, List<DexItem> items, List<V> values) { + private LirBuilder<V, B> addInstructionTemplate(int opcode, List<DexItem> items, List<V> values) { assert values.size() < MAX_VALUE_COUNT; int instructionIndex = advanceInstructionState(); int operandSize = 0; @@ -247,22 +248,22 @@ return this; } - public LIRBuilder<V, B> addConstNull() { - return addNoOperandInstruction(LIROpcodes.ACONST_NULL); + public LirBuilder<V, B> addConstNull() { + return addNoOperandInstruction(LirOpcodes.ACONST_NULL); } - public LIRBuilder<V, B> addConstInt(int value) { + public LirBuilder<V, B> addConstInt(int value) { if (-1 <= value && value <= 5) { - addNoOperandInstruction(LIROpcodes.ICONST_0 + value); + addNoOperandInstruction(LirOpcodes.ICONST_0 + value); } else { advanceInstructionState(); - writer.writeInstruction(LIROpcodes.ICONST, ByteUtils.intEncodingSize(value)); + writer.writeInstruction(LirOpcodes.ICONST, ByteUtils.intEncodingSize(value)); ByteUtils.writeEncodedInt(value, writer::writeOperand); } return this; } - public LIRBuilder<V, B> addConstNumber(ValueType type, long value) { + public LirBuilder<V, B> addConstNumber(ValueType type, long value) { switch (type) { case OBJECT: return addConstNull(); @@ -276,11 +277,11 @@ } } - public LIRBuilder<V, B> addConstString(DexString string) { - return addOneItemInstruction(LIROpcodes.LDC, string); + public LirBuilder<V, B> addConstString(DexString string) { + return addOneItemInstruction(LirOpcodes.LDC, string); } - public LIRBuilder<V, B> addDiv(NumericType type, V leftValue, V rightValue) { + public LirBuilder<V, B> addDiv(NumericType type, V leftValue, V rightValue) { switch (type) { case BYTE: case CHAR: @@ -288,7 +289,7 @@ case INT: { return addInstructionTemplate( - LIROpcodes.IDIV, Collections.emptyList(), ImmutableList.of(leftValue, rightValue)); + LirOpcodes.IDIV, Collections.emptyList(), ImmutableList.of(leftValue, rightValue)); } case LONG: case FLOAT: @@ -298,72 +299,72 @@ } } - public LIRBuilder<V, B> addArrayLength(V array) { - return addOneValueInstruction(LIROpcodes.ARRAYLENGTH, array); + public LirBuilder<V, B> addArrayLength(V array) { + return addOneValueInstruction(LirOpcodes.ARRAYLENGTH, array); } - public LIRBuilder<V, B> addStaticGet(DexField field) { - return addOneItemInstruction(LIROpcodes.GETSTATIC, field); + public LirBuilder<V, B> addStaticGet(DexField field) { + return addOneItemInstruction(LirOpcodes.GETSTATIC, field); } - public LIRBuilder<V, B> addInvokeInstruction(int opcode, DexMethod method, List<V> arguments) { + public LirBuilder<V, B> addInvokeInstruction(int opcode, DexMethod method, List<V> arguments) { return addInstructionTemplate(opcode, Collections.singletonList(method), arguments); } - public LIRBuilder<V, B> addInvokeDirect(DexMethod method, List<V> arguments) { - return addInvokeInstruction(LIROpcodes.INVOKEDIRECT, method, arguments); + public LirBuilder<V, B> addInvokeDirect(DexMethod method, List<V> arguments) { + return addInvokeInstruction(LirOpcodes.INVOKEDIRECT, method, arguments); } - public LIRBuilder<V, B> addInvokeVirtual(DexMethod method, List<V> arguments) { - return addInvokeInstruction(LIROpcodes.INVOKEVIRTUAL, method, arguments); + public LirBuilder<V, B> addInvokeVirtual(DexMethod method, List<V> arguments) { + return addInvokeInstruction(LirOpcodes.INVOKEVIRTUAL, method, arguments); } - public LIRBuilder<V, B> addReturn(V value) { + public LirBuilder<V, B> addReturn(V value) { throw new Unimplemented(); } - public LIRBuilder<V, B> addReturnVoid() { - return addNoOperandInstruction(LIROpcodes.RETURN); + public LirBuilder<V, B> addReturnVoid() { + return addNoOperandInstruction(LirOpcodes.RETURN); } - public LIRBuilder<V, B> addDebugPosition(Position position) { + public LirBuilder<V, B> addDebugPosition(Position position) { assert currentPosition == position; - return addNoOperandInstruction(LIROpcodes.DEBUGPOS); + return addNoOperandInstruction(LirOpcodes.DEBUGPOS); } public void addFallthrough() { - addNoOperandInstruction(LIROpcodes.FALLTHROUGH); + addNoOperandInstruction(LirOpcodes.FALLTHROUGH); } - public LIRBuilder<V, B> addGoto(B target) { + public LirBuilder<V, B> addGoto(B target) { int targetIndex = getBlockIndex(target); int operandSize = blockIndexSize(targetIndex); advanceInstructionState(); - writer.writeInstruction(LIROpcodes.GOTO, operandSize); + writer.writeInstruction(LirOpcodes.GOTO, operandSize); writeBlockIndex(targetIndex); return this; } - public LIRBuilder<V, B> addIf(Type ifKind, ValueType valueType, V value, B trueTarget) { + public LirBuilder<V, B> addIf(Type ifKind, ValueType valueType, V value, B trueTarget) { int opcode; switch (ifKind) { case EQ: - opcode = valueType.isObject() ? LIROpcodes.IFNULL : LIROpcodes.IFEQ; + opcode = valueType.isObject() ? LirOpcodes.IFNULL : LirOpcodes.IFEQ; break; case GE: - opcode = LIROpcodes.IFGE; + opcode = LirOpcodes.IFGE; break; case GT: - opcode = LIROpcodes.IFGT; + opcode = LirOpcodes.IFGT; break; case LE: - opcode = LIROpcodes.IFLE; + opcode = LirOpcodes.IFLE; break; case LT: - opcode = LIROpcodes.IFLT; + opcode = LirOpcodes.IFLT; break; case NE: - opcode = valueType.isObject() ? LIROpcodes.IFNONNULL : LIROpcodes.IFNE; + opcode = valueType.isObject() ? LirOpcodes.IFNONNULL : LirOpcodes.IFNE; break; default: throw new Unreachable("Unexpected if kind: " + ifKind); @@ -378,27 +379,27 @@ return this; } - public LIRBuilder<V, B> addIfCmp( + public LirBuilder<V, B> addIfCmp( Type ifKind, ValueType valueType, List<V> inValues, B trueTarget) { int opcode; switch (ifKind) { case EQ: - opcode = valueType.isObject() ? LIROpcodes.IF_ACMPEQ : LIROpcodes.IF_ICMPEQ; + opcode = valueType.isObject() ? LirOpcodes.IF_ACMPEQ : LirOpcodes.IF_ICMPEQ; break; case GE: - opcode = LIROpcodes.IF_ICMPGE; + opcode = LirOpcodes.IF_ICMPGE; break; case GT: - opcode = LIROpcodes.IF_ICMPGT; + opcode = LirOpcodes.IF_ICMPGT; break; case LE: - opcode = LIROpcodes.IF_ICMPLE; + opcode = LirOpcodes.IF_ICMPLE; break; case LT: - opcode = LIROpcodes.IF_ICMPLT; + opcode = LirOpcodes.IF_ICMPLT; break; case NE: - opcode = valueType.isObject() ? LIROpcodes.IF_ACMPNE : LIROpcodes.IF_ICMPNE; + opcode = valueType.isObject() ? LirOpcodes.IF_ACMPNE : LirOpcodes.IF_ICMPNE; break; default: throw new Unreachable("Unexpected if kind " + ifKind); @@ -418,27 +419,27 @@ return this; } - public LIRBuilder<V, B> addMoveException(DexType exceptionType) { - return addOneItemInstruction(LIROpcodes.MOVEEXCEPTION, exceptionType); + public LirBuilder<V, B> addMoveException(DexType exceptionType) { + return addOneItemInstruction(LirOpcodes.MOVEEXCEPTION, exceptionType); } - public LIRBuilder<V, B> addPhi(TypeElement type, List<V> operands) { + public LirBuilder<V, B> addPhi(TypeElement type, List<V> operands) { DexType dexType = toDexType(type); - return addInstructionTemplate(LIROpcodes.PHI, Collections.singletonList(dexType), operands); + return addInstructionTemplate(LirOpcodes.PHI, Collections.singletonList(dexType), operands); } - public LIRBuilder<V, B> addDebugLocalWrite(V src) { - return addOneValueInstruction(LIROpcodes.DEBUGLOCALWRITE, src); + public LirBuilder<V, B> addDebugLocalWrite(V src) { + return addOneValueInstruction(LirOpcodes.DEBUGLOCALWRITE, src); } - public LIRCode build() { + public LirCode build() { assert metadata != null; int constantsCount = constants.size(); DexItem[] constantTable = new DexItem[constantsCount]; constants.forEach((item, index) -> constantTable[index] = item); DebugLocalInfoTable debugTable = debugLocals.isEmpty() ? null : new DebugLocalInfoTable(debugLocals, debugLocalEnds); - return new LIRCode( + return new LirCode( metadata, constantTable, positionTable.toArray(new PositionEntry[positionTable.size()]), @@ -446,6 +447,7 @@ byteWriter.toByteArray(), instructionCount, new TryCatchTable(tryCatchRanges), - debugTable); + debugTable, + ssaValueStrategy); } }
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRCode.java b/src/main/java/com/android/tools/r8/lightir/LirCode.java similarity index 83% rename from src/main/java/com/android/tools/r8/lightir/LIRCode.java rename to src/main/java/com/android/tools/r8/lightir/LirCode.java index e39820a..3851ba5 100644 --- a/src/main/java/com/android/tools/r8/lightir/LIRCode.java +++ b/src/main/java/com/android/tools/r8/lightir/LirCode.java
@@ -10,11 +10,11 @@ import com.android.tools.r8.ir.code.CatchHandlers; import com.android.tools.r8.ir.code.IRMetadata; import com.android.tools.r8.ir.code.Position; -import com.android.tools.r8.lightir.LIRBuilder.BlockIndexGetter; -import com.android.tools.r8.lightir.LIRBuilder.ValueIndexGetter; +import com.android.tools.r8.lightir.LirBuilder.BlockIndexGetter; +import com.android.tools.r8.lightir.LirBuilder.ValueIndexGetter; import it.unimi.dsi.fastutil.ints.Int2ReferenceMap; -public class LIRCode implements Iterable<LIRInstructionView> { +public class LirCode implements Iterable<LirInstructionView> { public static class PositionEntry { final int fromInstructionIndex; @@ -52,6 +52,8 @@ } } + private final LirSsaValueStrategy ssaValueStrategy; + private final IRMetadata metadata; /** Constant pool of items. */ @@ -74,16 +76,16 @@ /** Table of debug local information for each SSA value (if present). */ private final DebugLocalInfoTable debugLocalInfoTable; - public static <V, B> LIRBuilder<V, B> builder( + public static <V, B> LirBuilder<V, B> builder( DexMethod method, ValueIndexGetter<V> valueIndexGetter, BlockIndexGetter<B> blockIndexGetter, DexItemFactory factory) { - return new LIRBuilder<V, B>(method, valueIndexGetter, blockIndexGetter, factory); + return new LirBuilder<V, B>(method, valueIndexGetter, blockIndexGetter, factory); } - // Should be constructed using LIRBuilder. - LIRCode( + /** Should be constructed using {@link LirBuilder}. */ + LirCode( IRMetadata metadata, DexItem[] constants, PositionEntry[] positions, @@ -91,7 +93,8 @@ byte[] instructions, int instructionCount, TryCatchTable tryCatchTable, - DebugLocalInfoTable debugLocalInfoTable) { + DebugLocalInfoTable debugLocalInfoTable, + LirSsaValueStrategy ssaValueStrategy) { this.metadata = metadata; this.constants = constants; this.positionTable = positions; @@ -100,6 +103,11 @@ this.instructionCount = instructionCount; this.tryCatchTable = tryCatchTable; this.debugLocalInfoTable = debugLocalInfoTable; + this.ssaValueStrategy = ssaValueStrategy; + } + + public int decodeValueIndex(int encodedValueIndex, int currentValueIndex) { + return ssaValueStrategy.decodeValueIndex(encodedValueIndex, currentValueIndex); } public int getArgumentCount() { @@ -145,12 +153,12 @@ } @Override - public LIRIterator iterator() { - return new LIRIterator(new ByteArrayIterator(instructions)); + public LirIterator iterator() { + return new LirIterator(new ByteArrayIterator(instructions)); } @Override public String toString() { - return new LIRPrinter(this).prettyPrint(); + return new LirPrinter(this).prettyPrint(); } }
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRInstructionCallback.java b/src/main/java/com/android/tools/r8/lightir/LirInstructionCallback.java similarity index 77% rename from src/main/java/com/android/tools/r8/lightir/LIRInstructionCallback.java rename to src/main/java/com/android/tools/r8/lightir/LirInstructionCallback.java index 42a0955..35b6910 100644 --- a/src/main/java/com/android/tools/r8/lightir/LIRInstructionCallback.java +++ b/src/main/java/com/android/tools/r8/lightir/LirInstructionCallback.java
@@ -4,7 +4,7 @@ package com.android.tools.r8.lightir; /** Convenience interface to accept a LIR instruction view. */ -public interface LIRInstructionCallback { +public interface LirInstructionCallback { - void onInstructionView(LIRInstructionView view); + void onInstructionView(LirInstructionView view); }
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRInstructionView.java b/src/main/java/com/android/tools/r8/lightir/LirInstructionView.java similarity index 88% rename from src/main/java/com/android/tools/r8/lightir/LIRInstructionView.java rename to src/main/java/com/android/tools/r8/lightir/LirInstructionView.java index 428c01f..3eef40d 100644 --- a/src/main/java/com/android/tools/r8/lightir/LIRInstructionView.java +++ b/src/main/java/com/android/tools/r8/lightir/LirInstructionView.java
@@ -10,15 +10,15 @@ * can change. The view callbacks allow interpreting the instruction at different levels of * abstraction depending on need. */ -public interface LIRInstructionView { +public interface LirInstructionView { /** Convenience method to forward control to a callback. */ - void accept(LIRInstructionCallback eventCallback); + void accept(LirInstructionCallback eventCallback); /** Get the instruction index. */ int getInstructionIndex(); - /** The opcode of the instruction (See {@code LIROpcodes} for values). */ + /** The opcode of the instruction (See {@link LirOpcodes} for values). */ int getOpcode(); /** The remaining size of the instruction's payload. */
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRIterator.java b/src/main/java/com/android/tools/r8/lightir/LirIterator.java similarity index 89% rename from src/main/java/com/android/tools/r8/lightir/LIRIterator.java rename to src/main/java/com/android/tools/r8/lightir/LirIterator.java index 882e960..dd29c93 100644 --- a/src/main/java/com/android/tools/r8/lightir/LIRIterator.java +++ b/src/main/java/com/android/tools/r8/lightir/LirIterator.java
@@ -12,7 +12,7 @@ * <p>This iterator is internally a zero-allocation parser with the "elements" as a view onto the * current state. */ -public class LIRIterator implements Iterator<LIRInstructionView>, LIRInstructionView { +public class LirIterator implements Iterator<LirInstructionView>, LirInstructionView { private final ByteIterator iterator; @@ -24,7 +24,7 @@ private int currentInstructionIndex = -1; private int currentOpcode = -1; - public LIRIterator(ByteIterator iterator) { + public LirIterator(ByteIterator iterator) { this.iterator = iterator; } @@ -41,11 +41,11 @@ } @Override - public LIRInstructionView next() { + public LirInstructionView next() { skipRemainingOperands(); ++currentInstructionIndex; currentOpcode = u1(); - if (LIROpcodes.isOneByteInstruction(currentOpcode)) { + if (LirOpcodes.isOneByteInstruction(currentOpcode)) { endOfCurrentInstruction = currentByteIndex; } else { // Any instruction that is not a single byte has a two-byte header. The second byte is the @@ -57,7 +57,7 @@ } @Override - public void accept(LIRInstructionCallback eventCallback) { + public void accept(LirInstructionCallback eventCallback) { eventCallback.onInstructionView(this); }
diff --git a/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java b/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java similarity index 99% rename from src/main/java/com/android/tools/r8/lightir/LIROpcodes.java rename to src/main/java/com/android/tools/r8/lightir/LirOpcodes.java index abfea89..0d30efd 100644 --- a/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java +++ b/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java
@@ -10,7 +10,7 @@ * * <p>The constants generally follow the bytecode values as defined by the classfile format. */ -public interface LIROpcodes { +public interface LirOpcodes { static boolean isOneByteInstruction(int opcode) { assert opcode >= ACONST_NULL;
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java similarity index 81% rename from src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java rename to src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java index 40395bb..dba40f8 100644 --- a/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java +++ b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
@@ -23,13 +23,13 @@ * onInvokeVirtual will default dispatch to onInvokedMethodInstruction). * * <p>Due to the parsing of the individual instructions, this parser has a higher overhead than - * using the basic {@code LIRInstructionView}. + * using the basic {@link LirInstructionView}. */ -public abstract class LIRParsedInstructionCallback implements LIRInstructionCallback { +public abstract class LirParsedInstructionCallback implements LirInstructionCallback { - private final LIRCode code; + private final LirCode code; - public LIRParsedInstructionCallback(LIRCode code) { + public LirParsedInstructionCallback(LirCode code) { this.code = code; } @@ -41,10 +41,10 @@ } private int getActualValueIndex(int relativeValueIndex) { - return LIRUtils.decodeValueIndex(relativeValueIndex, getCurrentValueIndex()); + return code.decodeValueIndex(relativeValueIndex, getCurrentValueIndex()); } - private int getNextValueOperand(LIRInstructionView view) { + private int getNextValueOperand(LirInstructionView view) { return getActualValueIndex(view.getNextValueOperand()); } @@ -135,15 +135,15 @@ } @Override - public void onInstructionView(LIRInstructionView view) { + public void onInstructionView(LirInstructionView view) { int opcode = view.getOpcode(); switch (opcode) { - case LIROpcodes.ACONST_NULL: + case LirOpcodes.ACONST_NULL: { onConstNull(); return; } - case LIROpcodes.LDC: + case LirOpcodes.LDC: { DexItem item = getConstantItem(view.getNextConstantOperand()); if (item instanceof DexString) { @@ -152,80 +152,80 @@ } throw new Unimplemented(); } - case LIROpcodes.ICONST_M1: - case LIROpcodes.ICONST_0: - case LIROpcodes.ICONST_1: - case LIROpcodes.ICONST_2: - case LIROpcodes.ICONST_3: - case LIROpcodes.ICONST_4: - case LIROpcodes.ICONST_5: + case LirOpcodes.ICONST_M1: + case LirOpcodes.ICONST_0: + case LirOpcodes.ICONST_1: + case LirOpcodes.ICONST_2: + case LirOpcodes.ICONST_3: + case LirOpcodes.ICONST_4: + case LirOpcodes.ICONST_5: { - int value = opcode - LIROpcodes.ICONST_0; + int value = opcode - LirOpcodes.ICONST_0; onConstInt(value); return; } - case LIROpcodes.ICONST: + case LirOpcodes.ICONST: { int value = view.getNextIntegerOperand(); onConstInt(value); return; } - case LIROpcodes.IDIV: + case LirOpcodes.IDIV: { int leftValueIndex = getNextValueOperand(view); int rightValueIndex = getNextValueOperand(view); onDivInt(leftValueIndex, rightValueIndex); return; } - case LIROpcodes.IFNE: + case LirOpcodes.IFNE: { int blockIndex = view.getNextBlockOperand(); int valueIndex = getNextValueOperand(view); onIf(If.Type.NE, blockIndex, valueIndex); return; } - case LIROpcodes.GOTO: + case LirOpcodes.GOTO: { int blockIndex = view.getNextBlockOperand(); onGoto(blockIndex); return; } - case LIROpcodes.INVOKEDIRECT: + case LirOpcodes.INVOKEDIRECT: { DexMethod target = getInvokeInstructionTarget(view); IntList arguments = getInvokeInstructionArguments(view); onInvokeDirect(target, arguments); return; } - case LIROpcodes.INVOKEVIRTUAL: + case LirOpcodes.INVOKEVIRTUAL: { DexMethod target = getInvokeInstructionTarget(view); IntList arguments = getInvokeInstructionArguments(view); onInvokeVirtual(target, arguments); return; } - case LIROpcodes.GETSTATIC: + case LirOpcodes.GETSTATIC: { DexField field = (DexField) getConstantItem(view.getNextConstantOperand()); onStaticGet(field); return; } - case LIROpcodes.RETURN: + case LirOpcodes.RETURN: { onReturnVoid(); return; } - case LIROpcodes.ARRAYLENGTH: + case LirOpcodes.ARRAYLENGTH: { onArrayLength(getNextValueOperand(view)); return; } - case LIROpcodes.DEBUGPOS: + case LirOpcodes.DEBUGPOS: { onDebugPosition(); return; } - case LIROpcodes.PHI: + case LirOpcodes.PHI: { DexType type = (DexType) getConstantItem(view.getNextConstantOperand()); IntList operands = new IntArrayList(); @@ -235,33 +235,33 @@ onPhi(type, operands); return; } - case LIROpcodes.FALLTHROUGH: + case LirOpcodes.FALLTHROUGH: { onFallthrough(); return; } - case LIROpcodes.MOVEEXCEPTION: + case LirOpcodes.MOVEEXCEPTION: { DexType type = (DexType) getConstantItem(view.getNextConstantOperand()); onMoveException(type); return; } - case LIROpcodes.DEBUGLOCALWRITE: + case LirOpcodes.DEBUGLOCALWRITE: { int srcIndex = getNextValueOperand(view); onDebugLocalWrite(srcIndex); return; } default: - throw new Unimplemented("No dispatch for opcode " + LIROpcodes.toString(opcode)); + throw new Unimplemented("No dispatch for opcode " + LirOpcodes.toString(opcode)); } } - private DexMethod getInvokeInstructionTarget(LIRInstructionView view) { + private DexMethod getInvokeInstructionTarget(LirInstructionView view) { return (DexMethod) getConstantItem(view.getNextConstantOperand()); } - private IntList getInvokeInstructionArguments(LIRInstructionView view) { + private IntList getInvokeInstructionArguments(LirInstructionView view) { IntList arguments = new IntArrayList(); while (view.hasMoreOperands()) { arguments.add(getNextValueOperand(view));
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRPrinter.java b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java similarity index 92% rename from src/main/java/com/android/tools/r8/lightir/LIRPrinter.java rename to src/main/java/com/android/tools/r8/lightir/LirPrinter.java index c8af439..93d117b 100644 --- a/src/main/java/com/android/tools/r8/lightir/LIRPrinter.java +++ b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
@@ -13,19 +13,19 @@ import com.android.tools.r8.utils.StringUtils; import it.unimi.dsi.fastutil.ints.IntList; -public class LIRPrinter extends LIRParsedInstructionCallback { +public class LirPrinter extends LirParsedInstructionCallback { private static final String SEPERATOR = "\n"; - private final LIRCode code; + private final LirCode code; private final StringBuilder builder = new StringBuilder(); private final int instructionIndexPadding; private final int instructionNamePadding; private int valueIndex = 0; - private LIRInstructionView view; + private LirInstructionView view; - public LIRPrinter(LIRCode code) { + public LirPrinter(LirCode code) { super(code); this.code = code; instructionIndexPadding = @@ -33,8 +33,8 @@ fmtInsnIndex(-code.getArgumentCount()).length(), fmtInsnIndex(code.getInstructionCount() - 1).length()); int maxNameLength = 0; - for (LIRInstructionView view : code) { - maxNameLength = Math.max(maxNameLength, LIROpcodes.toString(view.getOpcode()).length()); + for (LirInstructionView view : code) { + maxNameLength = Math.max(maxNameLength, LirOpcodes.toString(view.getOpcode()).length()); } instructionNamePadding = maxNameLength; } @@ -73,12 +73,12 @@ } @Override - public void onInstructionView(LIRInstructionView view) { + public void onInstructionView(LirInstructionView view) { this.view = view; assert view.getInstructionIndex() == getCurrentInstructionIndex(); int operandSizeInBytes = view.getRemainingOperandSizeInBytes(); int instructionSizeInBytes = operandSizeInBytes == 0 ? 1 : 2 + operandSizeInBytes; - String opcode = LIROpcodes.toString(view.getOpcode()); + String opcode = LirOpcodes.toString(view.getOpcode()); addInstructionHeader(opcode, instructionSizeInBytes); super.onInstructionView(view); advanceToNextValueIndex(); @@ -101,7 +101,7 @@ @Override public void onInstruction() { throw new Unimplemented( - "Printing of instruction missing: " + LIROpcodes.toString(view.getOpcode())); + "Printing of instruction missing: " + LirOpcodes.toString(view.getOpcode())); } private StringBuilder appendOutValue() {
diff --git a/src/main/java/com/android/tools/r8/lightir/LirSsaValueStrategy.java b/src/main/java/com/android/tools/r8/lightir/LirSsaValueStrategy.java new file mode 100644 index 0000000..b89dca8 --- /dev/null +++ b/src/main/java/com/android/tools/r8/lightir/LirSsaValueStrategy.java
@@ -0,0 +1,50 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.lightir; + +/** + * Abstraction for how to encode SSA values. + * + * <p>At high level the unencoded SSA value index is the absolute index to the instruction defining + * the SSA out value. This strategy provides a way to opaquely encode any references of an SSA value + * as the relative offset from the referencing instruction. + */ +public abstract class LirSsaValueStrategy { + + private static final LirSsaValueStrategy INSTANCE = new RelativeStrategy(); + + public static LirSsaValueStrategy get() { + return INSTANCE; + } + + public abstract int encodeValueIndex(int absoluteValueIndex, int currentValueIndex); + + public abstract int decodeValueIndex(int encodedValueIndex, int currentValueIndex); + + private static class AbsoluteStrategy extends LirSsaValueStrategy { + + @Override + public int encodeValueIndex(int absoluteValueIndex, int currentValueIndex) { + return absoluteValueIndex; + } + + @Override + public int decodeValueIndex(int encodedValueIndex, int currentValueIndex) { + return encodedValueIndex; + } + } + + private static class RelativeStrategy extends LirSsaValueStrategy { + + @Override + public int encodeValueIndex(int absoluteValueIndex, int currentValueIndex) { + return currentValueIndex - absoluteValueIndex; + } + + @Override + public int decodeValueIndex(int encodedValueIndex, int currentValueIndex) { + return currentValueIndex - encodedValueIndex; + } + } +}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRWriter.java b/src/main/java/com/android/tools/r8/lightir/LirWriter.java similarity index 86% rename from src/main/java/com/android/tools/r8/lightir/LIRWriter.java rename to src/main/java/com/android/tools/r8/lightir/LirWriter.java index 074726d..3f9e6b4 100644 --- a/src/main/java/com/android/tools/r8/lightir/LIRWriter.java +++ b/src/main/java/com/android/tools/r8/lightir/LirWriter.java
@@ -7,19 +7,19 @@ * Lowest level writer for constructing LIR encoded data. * * <p>This writer deals with just the instruction and operand encodings. For higher level structure, - * such as the constant pool, see LIRBuilder. + * such as the constant pool, see {@link LirBuilder}. */ -public class LIRWriter { +public class LirWriter { private final ByteWriter writer; private int pendingOperandBytes = 0; - public LIRWriter(ByteWriter writer) { + public LirWriter(ByteWriter writer) { this.writer = writer; } public void writeOneByteInstruction(int opcode) { - assert LIROpcodes.isOneByteInstruction(opcode); + assert LirOpcodes.isOneByteInstruction(opcode); assert pendingOperandBytes == 0; writer.put(ByteUtils.ensureU1(opcode)); }
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java index 2ac83b2..b6abede 100644 --- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java +++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -50,6 +50,7 @@ private final AndroidApiLevelCompute androidApiLevelCompute; private final AppView<AppInfoWithLiveness> appView; + private final MemberRebindingEventConsumer eventConsumer; private final InternalOptions options; private final MemberRebindingLens.Builder lensBuilder; @@ -58,6 +59,7 @@ assert appView.graphLens().isContextFreeForMethods(); this.androidApiLevelCompute = appView.apiLevelCompute(); this.appView = appView; + this.eventConsumer = MemberRebindingEventConsumer.create(appView); this.options = appView.options(); this.lensBuilder = MemberRebindingLens.builder(appView); } @@ -383,6 +385,8 @@ target.isLibraryMethod(), OptionalBool.TRUE); }); bridgeHolder.addMethod(bridgeMethodDefinition); + eventConsumer.acceptMemberRebindingBridgeMethod( + bridgeMethodDefinition.asProgramMethod(bridgeHolder), target); } assert resolver.apply(method).getResolvedMethod().getReference() == bridgeMethod; } @@ -507,12 +511,14 @@ fieldAccessInfoCollection.forEach(lensBuilder::recordNonReboundFieldAccesses); } - public MemberRebindingLens run(ExecutorService executorService) throws ExecutionException { + public void run(ExecutorService executorService) throws ExecutionException { AppInfoWithLiveness appInfo = appView.appInfo(); computeMethodRebinding(appInfo.getMethodAccessInfoCollection()); recordNonReboundFieldAccesses(executorService); appInfo.getFieldAccessInfoCollection().flattenAccessContexts(); - return lensBuilder.build(); + MemberRebindingLens memberRebindingLens = lensBuilder.build(); + appView.setGraphLens(memberRebindingLens); + eventConsumer.finished(appView, memberRebindingLens); } private boolean verifyFieldAccessCollectionContainsAllNonReboundFieldReferences(
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingEventConsumer.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingEventConsumer.java new file mode 100644 index 0000000..dff6597 --- /dev/null +++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingEventConsumer.java
@@ -0,0 +1,46 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.optimize; + +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexClassAndMethod; +import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingMemberRebindingEventConsumer; +import com.android.tools.r8.shaking.AppInfoWithLiveness; + +public interface MemberRebindingEventConsumer { + + void acceptMemberRebindingBridgeMethod( + ProgramMethod bridgeMethod, DexClassAndMethod targetMethod); + + default void finished( + AppView<AppInfoWithLiveness> appView, MemberRebindingLens memberRebindingLens) {} + + static MemberRebindingEventConsumer create(AppView<AppInfoWithLiveness> appView) { + return ArtProfileRewritingMemberRebindingEventConsumer.attach(appView, empty()); + } + + static EmptyMemberRebindingEventConsumer empty() { + return EmptyMemberRebindingEventConsumer.getInstance(); + } + + class EmptyMemberRebindingEventConsumer implements MemberRebindingEventConsumer { + + private static final EmptyMemberRebindingEventConsumer INSTANCE = + new EmptyMemberRebindingEventConsumer(); + + private EmptyMemberRebindingEventConsumer() {} + + static EmptyMemberRebindingEventConsumer getInstance() { + return INSTANCE; + } + + @Override + public void acceptMemberRebindingBridgeMethod( + ProgramMethod bridgeMethod, DexClassAndMethod targetMethod) { + // Intentionally empty. + } + } +}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java index 16edb6a..1a2eaa6 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -36,7 +36,6 @@ import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo; import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorGraphLens.Builder; import com.android.tools.r8.optimize.argumentpropagation.utils.ParameterRemovalUtils; -import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.shaking.KeepFieldInfo; import com.android.tools.r8.shaking.KeepMethodInfo; @@ -194,10 +193,8 @@ Timing timing) throws ExecutionException { timing.begin("Optimize components"); - ArtProfileCollectionAdditions artProfileCollectionAdditions = - ArtProfileCollectionAdditions.create(appView); ArgumentPropagatorSyntheticEventConsumer eventConsumer = - ArgumentPropagatorSyntheticEventConsumer.create(artProfileCollectionAdditions); + ArgumentPropagatorSyntheticEventConsumer.create(appView); ProcessorContext processorContext = appView.createProcessorContext(); Collection<Builder> partialGraphLensBuilders = ThreadUtils.processItemsWithResults( @@ -210,7 +207,7 @@ classes, DexMethodSignatureSet.empty()), affectedClassConsumer), executorService); - artProfileCollectionAdditions.commit(appView); + eventConsumer.finished(appView); timing.end(); // Merge all the partial, disjoint graph lens builders into a single graph lens.
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorSyntheticEventConsumer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorSyntheticEventConsumer.java index aae0602..799cd92 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorSyntheticEventConsumer.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorSyntheticEventConsumer.java
@@ -4,28 +4,20 @@ package com.android.tools.r8.optimize.argumentpropagation; +import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.ProgramMethod; -import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions; -import com.android.tools.r8.profile.art.rewriting.ConcreteArtProfileCollectionAdditions; +import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingArgumentPropagatorSyntheticEventConsumer; +import com.android.tools.r8.shaking.AppInfoWithLiveness; public interface ArgumentPropagatorSyntheticEventConsumer { void acceptInitializerArgumentClass(DexProgramClass clazz, ProgramMethod context); - static ArgumentPropagatorSyntheticEventConsumer create( - ArtProfileCollectionAdditions collectionAdditions) { - if (collectionAdditions.isNop()) { - return empty(); - } - return create(collectionAdditions.asConcrete()); - } + void finished(AppView<AppInfoWithLiveness> appView); - static ArgumentPropagatorSyntheticEventConsumer create( - ConcreteArtProfileCollectionAdditions collectionAdditions) { - return (clazz, context) -> - collectionAdditions.applyIfContextIsInProfile( - context, additionsBuilder -> additionsBuilder.addRule(clazz)); + static ArgumentPropagatorSyntheticEventConsumer create(AppView<AppInfoWithLiveness> appView) { + return ArtProfileRewritingArgumentPropagatorSyntheticEventConsumer.attach(appView, empty()); } static ArgumentPropagatorSyntheticEventConsumer empty() { @@ -48,5 +40,10 @@ public void acceptInitializerArgumentClass(DexProgramClass clazz, ProgramMethod context) { // Intentionally empty. } + + @Override + public void finished(AppView<AppInfoWithLiveness> appView) { + // Intentionally empty. + } } }
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java index 55d7ef6..80a9913 100644 --- a/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java +++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java
@@ -8,16 +8,19 @@ import com.android.tools.r8.TextInputStream; import com.android.tools.r8.TextOutputStream; +import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexReference; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.GraphLens; import com.android.tools.r8.graph.PrunedItems; +import com.android.tools.r8.ir.optimize.enums.EnumUnboxingLens; import com.android.tools.r8.naming.NamingLens; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.Reporter; import com.android.tools.r8.utils.ThrowingConsumer; +import com.android.tools.r8.utils.TriConsumer; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.UncheckedIOException; @@ -77,24 +80,60 @@ return (ArtProfileMethodRule) rules.get(method); } - public ArtProfile rewrittenWithLens(GraphLens lens) { + public ArtProfile rewrittenWithLens(AppView<?> appView, GraphLens lens) { + if (lens.isEnumUnboxerLens()) { + return rewrittenWithLens(appView, lens.asEnumUnboxerLens()); + } return transform( - (classRule, builderFactory) -> builderFactory.accept(lens.lookupType(classRule.getType())), - (methodRule, builderFactory) -> - builderFactory + (classRule, classRuleBuilderFactory) -> { + DexType newClassRule = lens.lookupType(classRule.getType()); + assert newClassRule.isClassType(); + classRuleBuilderFactory.accept(newClassRule); + }, + (methodRule, classRuleBuilderFactory, methodRuleBuilderFactory) -> + methodRuleBuilderFactory .apply(lens.getRenamedMethodSignature(methodRule.getMethod())) .acceptMethodRuleInfoBuilder( methodRuleInfoBuilder -> methodRuleInfoBuilder.merge(methodRule.getMethodRuleInfo()))); } + public ArtProfile rewrittenWithLens(AppView<?> appView, EnumUnboxingLens lens) { + return transform( + (classRule, classRuleBuilderFactory) -> { + DexType newClassRule = lens.lookupType(classRule.getType()); + if (newClassRule.isClassType()) { + classRuleBuilderFactory.accept(newClassRule); + } else { + assert newClassRule.isIntType(); + } + }, + (methodRule, classRuleBuilderFactory, methodRuleBuilderFactory) -> { + DexMethod newMethod = lens.getRenamedMethodSignature(methodRule.getMethod()); + // When moving non-synthetic methods from an enum class to its enum utility class we also + // add a rule for the utility class. + if (newMethod.getHolderType() != methodRule.getMethod().getHolderType()) { + assert appView + .getSyntheticItems() + .isSyntheticOfKind( + newMethod.getHolderType(), naming -> naming.ENUM_UNBOXING_LOCAL_UTILITY_CLASS); + classRuleBuilderFactory.accept(newMethod.getHolderType()); + } + methodRuleBuilderFactory + .apply(newMethod) + .acceptMethodRuleInfoBuilder( + methodRuleInfoBuilder -> + methodRuleInfoBuilder.merge(methodRule.getMethodRuleInfo())); + }); + } + public ArtProfile rewrittenWithLens(NamingLens lens, DexItemFactory dexItemFactory) { assert !lens.isIdentityLens(); return transform( - (classRule, builderFactory) -> - builderFactory.accept(lens.lookupType(classRule.getType(), dexItemFactory)), - (methodRule, builderFactory) -> - builderFactory + (classRule, classRuleBuilderFactory) -> + classRuleBuilderFactory.accept(lens.lookupType(classRule.getType(), dexItemFactory)), + (methodRule, classRuleBuilderFactory, methodRuleBuilderFactory) -> + methodRuleBuilderFactory .apply(lens.lookupMethod(methodRule.getMethod(), dexItemFactory)) .acceptMethodRuleInfoBuilder( methodRuleInfoBuilder -> @@ -103,14 +142,14 @@ public ArtProfile withoutPrunedItems(PrunedItems prunedItems) { return transform( - (classRule, builderFactory) -> { + (classRule, classRuleBuilderFactory) -> { if (!prunedItems.isRemoved(classRule.getType())) { - builderFactory.accept(classRule.getType()); + classRuleBuilderFactory.accept(classRule.getType()); } }, - (methodRule, builderFactory) -> { + (methodRule, classRuleBuilderFactory, methodRuleBuilderFactory) -> { if (!prunedItems.isRemoved(methodRule.getMethod())) { - builderFactory + methodRuleBuilderFactory .apply(methodRule.getMethod()) .acceptMethodRuleInfoBuilder( methodRuleInfoBuilder -> @@ -121,34 +160,35 @@ private ArtProfile transform( BiConsumer<ArtProfileClassRule, Consumer<DexType>> classTransformation, - BiConsumer<ArtProfileMethodRule, Function<DexMethod, ArtProfileMethodRule.Builder>> + TriConsumer< + ArtProfileMethodRule, + Consumer<DexType>, + Function<DexMethod, ArtProfileMethodRule.Builder>> methodTransformation) { Map<DexReference, ArtProfileRule.Builder> ruleBuilders = new LinkedHashMap<>(); + Consumer<DexType> classRuleBuilderFactory = + newType -> + ruleBuilders + .computeIfAbsent( + newType, ignoreKey(() -> ArtProfileClassRule.builder().setType(newType))) + .asClassRuleBuilder(); + Function<DexMethod, ArtProfileMethodRule.Builder> methodRuleBuilderFactory = + newMethod -> + ruleBuilders + .computeIfAbsent( + newMethod, ignoreKey(() -> ArtProfileMethodRule.builder().setMethod(newMethod))) + .asMethodRuleBuilder(); forEachRule( // Supply a factory method for creating a builder. If the current rule should be included in // the rewritten profile, the caller should call the provided builder factory method to // create a class rule builder. If two rules are mapped to the same reference, the same rule // builder is reused so that the two rules are merged into a single rule (with their flags // merged). - classRule -> - classTransformation.accept( - classRule, - newType -> - ruleBuilders - .computeIfAbsent( - newType, - ignoreKey(() -> ArtProfileClassRule.builder().setType(newType))) - .asClassRuleBuilder()), + classRule -> classTransformation.accept(classRule, classRuleBuilderFactory), // As above. methodRule -> methodTransformation.accept( - methodRule, - newMethod -> - ruleBuilders - .computeIfAbsent( - newMethod, - ignoreKey(() -> ArtProfileMethodRule.builder().setMethod(newMethod))) - .asMethodRuleBuilder())); + methodRule, classRuleBuilderFactory, methodRuleBuilderFactory)); return builder().addRuleBuilders(ruleBuilders.values()).build(); }
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java index e7012b8..87b0e95 100644 --- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java +++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java
@@ -65,7 +65,7 @@ public abstract NonEmptyArtProfileCollection asNonEmpty(); - public abstract ArtProfileCollection rewrittenWithLens(GraphLens lens); + public abstract ArtProfileCollection rewrittenWithLens(AppView<?> appView, GraphLens lens); public abstract ArtProfileCollection rewrittenWithLens( NamingLens lens, DexItemFactory dexItemFactory);
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileCompletenessChecker.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileCompletenessChecker.java index 21a7635..d7fde7d 100644 --- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileCompletenessChecker.java +++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileCompletenessChecker.java
@@ -19,6 +19,21 @@ import java.util.List; import java.util.Set; +/** + * Verifies that, if given an ART profile containing all program classes and methods in the input, + * then the residual ART profile also contains all classes and methods in the output. + * + * <p>If this check fails, either: + * + * <ul> + * <li>The current change added new synthetics to the program that need to be added to each + * profile that contains the synthesizing context, or + * <li>The existing rewriting of ART profiles and inclusion of synthetics in incomplete. + * </ul> + * + * <p>In the latter case, create a tracking bug and suppress the assertion failure by setting {@link + * ArtProfileOptions#setEnableCompletenessCheckForTesting(boolean)} to false. + */ public class ArtProfileCompletenessChecker { public enum CompletenessExceptions {
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRule.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRule.java index df469c9..fe7bd78 100644 --- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRule.java +++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRule.java
@@ -113,6 +113,10 @@ this.dexItemFactory = dexItemFactory; } + ArtProfileMethodRuleInfoImpl.Builder getMethodRuleInfoBuilder() { + return methodRuleInfoBuilder; + } + @Override public boolean isMethodRuleBuilder() { return true;
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfoImpl.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfoImpl.java index 252cdac..cb482d8 100644 --- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfoImpl.java +++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfoImpl.java
@@ -25,6 +25,10 @@ return EMPTY; } + public int getFlags() { + return flags; + } + public boolean isEmpty() { return flags == 0; } @@ -97,6 +101,10 @@ return this; } + public int getFlags() { + return flags; + } + public Builder merge(ArtProfileMethodRuleInfo methodRuleInfo) { if (methodRuleInfo.isHot()) { setIsHot(); @@ -141,7 +149,12 @@ } public Builder joinFlags(ArtProfileMethodRuleInfoImpl methodRuleInfo) { - flags |= methodRuleInfo.flags; + flags |= methodRuleInfo.getFlags(); + return this; + } + + public Builder joinFlags(ArtProfileMethodRule.Builder methodRuleBuilder) { + flags |= methodRuleBuilder.getMethodRuleInfoBuilder().getFlags(); return this; }
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java index d7c3f12..d5f91da 100644 --- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java +++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java
@@ -6,24 +6,34 @@ import static com.android.tools.r8.utils.SystemPropertyUtils.parseSystemPropertyOrDefault; +import com.android.tools.r8.utils.InternalOptions; import java.util.Collection; import java.util.Collections; public class ArtProfileOptions { + public static final String COMPLETENESS_PROPERTY_KEY = + "com.android.tools.r8.artprofilerewritingcompletenesscheck"; + private Collection<ArtProfileForRewriting> artProfilesForRewriting = Collections.emptyList(); private boolean enableCompletenessCheckForTesting = - parseSystemPropertyOrDefault( - "com.android.tools.r8.artprofilerewritingcompletenesscheck", false); + parseSystemPropertyOrDefault(COMPLETENESS_PROPERTY_KEY, false); - public ArtProfileOptions() {} + private final InternalOptions options; + + public ArtProfileOptions(InternalOptions options) { + this.options = options; + } public Collection<ArtProfileForRewriting> getArtProfilesForRewriting() { return artProfilesForRewriting; } public boolean isCompletenessCheckForTestingEnabled() { - return enableCompletenessCheckForTesting; + return enableCompletenessCheckForTesting + && !options.isDesugaredLibraryCompilation() + && !options.getStartupOptions().isStartupCompletenessCheckForTestingEnabled() + && !options.getStartupInstrumentationOptions().isStartupInstrumentationEnabled(); } public boolean isIncludingApiReferenceStubs() { @@ -33,6 +43,36 @@ return enableCompletenessCheckForTesting; } + public boolean isIncludingBackportedClasses() { + // Similar to isIncludingVarHandleClasses(). + return enableCompletenessCheckForTesting; + } + + public boolean isIncludingConstantDynamicClass() { + // Similar to isIncludingVarHandleClasses(). + return enableCompletenessCheckForTesting; + } + + public boolean isIncludingDesugaredLibraryRetargeterForwardingMethodsUnconditionally() { + // TODO(b/265729283): If we get as input the profile for the desugared library, maybe we can + // tell if the method targeted by the forwarding method is in the profile, e.g.: + // java.time.Instant java.util.DesugarDate.toInstant(java.util.Date). + return enableCompletenessCheckForTesting; + } + + public boolean isIncludingThrowingMethods() { + // The throw methods we insert should generally be dead a runtime, so no need for them to be + // optimized. + return enableCompletenessCheckForTesting; + } + + public boolean isIncludingVarHandleClasses() { + // We only include var handle classes in the residual ART profiles for completeness testing, + // since the classes synthesized by var handle desugaring are fairly large and may not be that + // important for runtime performance. + return enableCompletenessCheckForTesting; + } + public ArtProfileOptions setArtProfilesForRewriting(Collection<ArtProfileForRewriting> inputs) { this.artProfilesForRewriting = inputs; return this;
diff --git a/src/main/java/com/android/tools/r8/profile/art/EmptyArtProfileCollection.java b/src/main/java/com/android/tools/r8/profile/art/EmptyArtProfileCollection.java index 0540aa4..d4f3f9f 100644 --- a/src/main/java/com/android/tools/r8/profile/art/EmptyArtProfileCollection.java +++ b/src/main/java/com/android/tools/r8/profile/art/EmptyArtProfileCollection.java
@@ -31,7 +31,7 @@ } @Override - public ArtProfileCollection rewrittenWithLens(GraphLens lens) { + public ArtProfileCollection rewrittenWithLens(AppView<?> appView, GraphLens lens) { return this; }
diff --git a/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java b/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java index d7f72ef..5251d56 100644 --- a/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java +++ b/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java
@@ -46,8 +46,8 @@ } @Override - public NonEmptyArtProfileCollection rewrittenWithLens(GraphLens lens) { - return map(artProfile -> artProfile.rewrittenWithLens(lens)); + public NonEmptyArtProfileCollection rewrittenWithLens(AppView<?> appView, GraphLens lens) { + return map(artProfile -> artProfile.rewrittenWithLens(appView, lens)); } @Override
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileAdditions.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileAdditions.java index b210019..f4400c7 100644 --- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileAdditions.java +++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileAdditions.java
@@ -4,6 +4,8 @@ package com.android.tools.r8.profile.art.rewriting; +import static com.android.tools.r8.utils.MapUtils.ignoreKey; + import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexMethod; @@ -16,26 +18,46 @@ import com.android.tools.r8.profile.art.ArtProfileMethodRule; import com.android.tools.r8.profile.art.ArtProfileMethodRuleInfoImpl; import com.android.tools.r8.profile.art.ArtProfileRule; +import com.android.tools.r8.utils.WorkList; import com.google.common.collect.Sets; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import java.util.function.Function; +import java.util.stream.Collectors; /** Mutable extension of an existing ArtProfile. */ public class ArtProfileAdditions { public interface ArtProfileAdditionsBuilder { - ArtProfileAdditionsBuilder addRule(ProgramDefinition definition); + default ArtProfileAdditionsBuilder addRule(ProgramDefinition definition) { + return addRule(definition.getReference()); + } - ArtProfileAdditionsBuilder addRule(DexReference reference); + default ArtProfileAdditionsBuilder addRule(DexReference reference) { + if (reference.isDexType()) { + return addClassRule(reference.asDexType()); + } else { + assert reference.isDexMethod(); + return addMethodRule(reference.asDexMethod()); + } + } - ArtProfileAdditionsBuilder removeMovedMethodRule( - ProgramMethod oldMethod, ProgramMethod newMethod); + ArtProfileAdditionsBuilder addClassRule(DexType type); + + ArtProfileAdditionsBuilder addMethodRule(DexMethod method); + + default ArtProfileAdditionsBuilder removeMovedMethodRule( + ProgramMethod oldMethod, ProgramMethod newMethod) { + return removeMovedMethodRule(oldMethod.getReference(), newMethod); + } + + ArtProfileAdditionsBuilder removeMovedMethodRule(DexMethod oldMethod, ProgramMethod newMethod); } private ArtProfile artProfile; @@ -46,12 +68,15 @@ new ConcurrentHashMap<>(); private final Set<DexMethod> methodRuleRemovals = Sets.newConcurrentHashSet(); + private final NestedMethodRuleAdditionsGraph nestedMethodRuleAdditionsGraph = + new NestedMethodRuleAdditionsGraph(); + ArtProfileAdditions(ArtProfile artProfile) { this.artProfile = artProfile; } void applyIfContextIsInProfile(DexType context, Consumer<ArtProfileAdditions> fn) { - if (artProfile.containsClassRule(context)) { + if (artProfile.containsClassRule(context) || classRuleAdditions.containsKey(context)) { fn.accept(this); } } @@ -64,36 +89,54 @@ new ArtProfileAdditionsBuilder() { @Override - public ArtProfileAdditionsBuilder addRule(ProgramDefinition definition) { - return addRule(definition.getReference()); + public ArtProfileAdditionsBuilder addClassRule(DexType type) { + ArtProfileAdditions.this.addClassRule(type); + return this; } @Override - public ArtProfileAdditionsBuilder addRule(DexReference reference) { - addRuleFromContext( - reference, contextMethodRule, MethodRuleAdditionConfig.getDefault()); + public ArtProfileAdditionsBuilder addMethodRule(DexMethod method) { + ArtProfileAdditions.this.addMethodRuleFromContext( + method, + methodRuleInfoBuilder -> + methodRuleInfoBuilder.joinFlags(contextMethodRule.getMethodRuleInfo())); return this; } @Override public ArtProfileAdditionsBuilder removeMovedMethodRule( - ProgramMethod oldMethod, ProgramMethod newMethod) { + DexMethod oldMethod, ProgramMethod newMethod) { ArtProfileAdditions.this.removeMovedMethodRule(oldMethod, newMethod); return this; } }); - } - } + } else if (methodRuleAdditions.containsKey(context)) { + builderConsumer.accept( + new ArtProfileAdditionsBuilder() { - private void addRuleFromContext( - DexReference reference, - ArtProfileMethodRule contextMethodRule, - MethodRuleAdditionConfig config) { - if (reference.isDexType()) { - addClassRule(reference.asDexType()); - } else { - assert reference.isDexMethod(); - addMethodRuleFromContext(reference.asDexMethod(), contextMethodRule, config); + @Override + public ArtProfileAdditionsBuilder addClassRule(DexType type) { + ArtProfileAdditions.this.addClassRule(type); + return this; + } + + @Override + public ArtProfileAdditionsBuilder addMethodRule(DexMethod method) { + ArtProfileMethodRule.Builder contextRuleBuilder = methodRuleAdditions.get(context); + ArtProfileAdditions.this.addMethodRuleFromContext( + method, + methodRuleInfoBuilder -> methodRuleInfoBuilder.joinFlags(contextRuleBuilder)); + nestedMethodRuleAdditionsGraph.recordMethodRuleInfoFlagsLargerThan(method, context); + return this; + } + + @Override + public ArtProfileAdditionsBuilder removeMovedMethodRule( + DexMethod oldMethod, ProgramMethod newMethod) { + ArtProfileAdditions.this.removeMovedMethodRule(oldMethod, newMethod); + return this; + } + }); } } @@ -112,11 +155,9 @@ } private void addMethodRuleFromContext( - DexMethod method, ArtProfileMethodRule contextMethodRule, MethodRuleAdditionConfig config) { - addMethodRule( - method, - methodRuleInfoBuilder -> - config.configureMethodRuleInfo(methodRuleInfoBuilder, contextMethodRule)); + DexMethod method, + Consumer<ArtProfileMethodRuleInfoImpl.Builder> methodRuleInfoBuilderConsumer) { + addMethodRule(method, methodRuleInfoBuilderConsumer); } public ArtProfileAdditions addMethodRule( @@ -141,10 +182,10 @@ return this; } - void removeMovedMethodRule(ProgramMethod oldMethod, ProgramMethod newMethod) { - assert artProfile.containsMethodRule(oldMethod.getReference()); + void removeMovedMethodRule(DexMethod oldMethod, ProgramMethod newMethod) { + assert artProfile.containsMethodRule(oldMethod) || methodRuleAdditions.containsKey(oldMethod); assert methodRuleAdditions.containsKey(newMethod.getReference()); - methodRuleRemovals.add(oldMethod.getReference()); + methodRuleRemovals.add(oldMethod); } ArtProfile createNewArtProfile() { @@ -153,12 +194,27 @@ return artProfile; } + nestedMethodRuleAdditionsGraph.propagateMethodRuleInfoFlags(methodRuleAdditions); + // Add existing rules to new profile. ArtProfile.Builder artProfileBuilder = ArtProfile.builder(); artProfile.forEachRule( artProfileBuilder::addRule, methodRule -> { - if (!methodRuleRemovals.contains(methodRule.getMethod())) { + if (methodRuleRemovals.contains(methodRule.getMethod())) { + return; + } + ArtProfileMethodRule.Builder methodRuleBuilder = + methodRuleAdditions.remove(methodRule.getReference()); + if (methodRuleBuilder != null) { + ArtProfileMethodRule newMethodRule = + methodRuleBuilder + .acceptMethodRuleInfoBuilder( + methodRuleInfoBuilder -> + methodRuleInfoBuilder.joinFlags(methodRule.getMethodRuleInfo())) + .build(); + artProfileBuilder.addRule(newMethodRule); + } else { artProfileBuilder.addRule(methodRule); } }); @@ -205,4 +261,49 @@ void setArtProfile(ArtProfile artProfile) { this.artProfile = artProfile; } + + private static class NestedMethodRuleAdditionsGraph { + + private final Map<DexMethod, Set<DexMethod>> successors = new ConcurrentHashMap<>(); + private final Map<DexMethod, Set<DexMethod>> predecessors = new ConcurrentHashMap<>(); + + void recordMethodRuleInfoFlagsLargerThan(DexMethod largerFlags, DexMethod smallerFlags) { + predecessors + .computeIfAbsent(largerFlags, ignoreKey(Sets::newConcurrentHashSet)) + .add(smallerFlags); + successors + .computeIfAbsent(smallerFlags, ignoreKey(Sets::newConcurrentHashSet)) + .add(largerFlags); + } + + void propagateMethodRuleInfoFlags( + Map<DexMethod, ArtProfileMethodRule.Builder> methodRuleAdditions) { + List<DexMethod> leaves = + successors.keySet().stream() + .filter(method -> predecessors.getOrDefault(method, Collections.emptySet()).isEmpty()) + .collect(Collectors.toList()); + WorkList<DexMethod> worklist = WorkList.newIdentityWorkList(leaves); + while (worklist.hasNext()) { + DexMethod method = worklist.next(); + ArtProfileMethodRule.Builder methodRuleBuilder = methodRuleAdditions.get(method); + for (DexMethod successor : successors.getOrDefault(method, Collections.emptySet())) { + methodRuleAdditions + .get(successor) + .acceptMethodRuleInfoBuilder( + methodRuleInfoBuilder -> { + int oldFlags = methodRuleInfoBuilder.getFlags(); + methodRuleInfoBuilder.joinFlags(methodRuleBuilder); + // If this assertion fails, that means we have synthetics with multiple + // synthesizing contexts, which are not guaranteed to be processed before the + // synthetic itself. In that case this assertion should simply be removed. + assert methodRuleInfoBuilder.getFlags() == oldFlags; + }); + // Note: no need to addIgnoringSeenSet() since the graph will not have cycles. Indeed, it + // should never be the case that a method m2(), which is synthesized from method context + // m1(), would itself be a synthesizing context for m1(). + worklist.addIfNotSeen(successor); + } + } + } + } }
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileCollectionAdditions.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileCollectionAdditions.java index 36d235f..5198d05 100644 --- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileCollectionAdditions.java +++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileCollectionAdditions.java
@@ -6,6 +6,7 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.profile.art.ArtProfileCollection; import com.android.tools.r8.profile.art.rewriting.ArtProfileAdditions.ArtProfileAdditionsBuilder; import java.util.function.Consumer; @@ -34,6 +35,8 @@ return NopArtProfileCollectionAdditions.getInstance(); } + public abstract void addMethodIfContextIsInProfile(ProgramMethod method, ProgramMethod context); + public abstract void applyIfContextIsInProfile( DexMethod context, Consumer<ArtProfileAdditionsBuilder> builderConsumer); @@ -52,4 +55,6 @@ public abstract ArtProfileCollectionAdditions setArtProfileCollection( ArtProfileCollection artProfileCollection); + + public abstract boolean verifyIsCommitted(); }
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingApiReferenceStubberEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingApiReferenceStubberEventConsumer.java index 683bb93..0b4dfde 100644 --- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingApiReferenceStubberEventConsumer.java +++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingApiReferenceStubberEventConsumer.java
@@ -24,11 +24,17 @@ this.parent = parent; } - public static ArtProfileRewritingApiReferenceStubberEventConsumer attach( - ConcreteArtProfileCollectionAdditions collectionAdditions, - ApiReferenceStubberEventConsumer eventConsumer) { - return new ArtProfileRewritingApiReferenceStubberEventConsumer( - collectionAdditions, eventConsumer); + public static ApiReferenceStubberEventConsumer attach( + AppView<?> appView, ApiReferenceStubberEventConsumer eventConsumer) { + if (appView.options().getArtProfileOptions().isIncludingApiReferenceStubs()) { + ArtProfileCollectionAdditions artProfileCollectionAdditions = + ArtProfileCollectionAdditions.create(appView); + if (!artProfileCollectionAdditions.isNop()) { + return new ArtProfileRewritingApiReferenceStubberEventConsumer( + artProfileCollectionAdditions.asConcrete(), eventConsumer); + } + } + return eventConsumer; } @Override
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingArgumentPropagatorSyntheticEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingArgumentPropagatorSyntheticEventConsumer.java new file mode 100644 index 0000000..1084d3a --- /dev/null +++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingArgumentPropagatorSyntheticEventConsumer.java
@@ -0,0 +1,50 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.profile.art.rewriting; + +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorSyntheticEventConsumer; +import com.android.tools.r8.shaking.AppInfoWithLiveness; + +public class ArtProfileRewritingArgumentPropagatorSyntheticEventConsumer + implements ArgumentPropagatorSyntheticEventConsumer { + + private final ConcreteArtProfileCollectionAdditions additionsCollection; + private final ArgumentPropagatorSyntheticEventConsumer parent; + + private ArtProfileRewritingArgumentPropagatorSyntheticEventConsumer( + ConcreteArtProfileCollectionAdditions additionsCollection, + ArgumentPropagatorSyntheticEventConsumer parent) { + this.additionsCollection = additionsCollection; + this.parent = parent; + } + + public static ArgumentPropagatorSyntheticEventConsumer attach( + AppView<AppInfoWithLiveness> appView, + ArgumentPropagatorSyntheticEventConsumer eventConsumer) { + ArtProfileCollectionAdditions additionsCollection = + ArtProfileCollectionAdditions.create(appView); + if (additionsCollection.isNop()) { + return eventConsumer; + } + return new ArtProfileRewritingArgumentPropagatorSyntheticEventConsumer( + additionsCollection.asConcrete(), eventConsumer); + } + + @Override + public void acceptInitializerArgumentClass(DexProgramClass clazz, ProgramMethod context) { + additionsCollection.applyIfContextIsInProfile( + context, additionsBuilder -> additionsBuilder.addRule(clazz)); + parent.acceptInitializerArgumentClass(clazz, context); + } + + @Override + public void finished(AppView<AppInfoWithLiveness> appView) { + additionsCollection.commit(appView); + parent.finished(appView); + } +}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer.java index 3cc4631..ebc4799 100644 --- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer.java +++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer.java
@@ -4,37 +4,55 @@ package com.android.tools.r8.profile.art.rewriting; +import static com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingVarHandleDesugaringEventConsumerUtils.handleVarHandleDesugaringClassContext; + +import com.android.tools.r8.graph.AppInfoWithClassHierarchy; +import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.ProgramDefinition; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer; +import com.android.tools.r8.profile.art.ArtProfileOptions; import java.util.Set; public class ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer extends CfClassSynthesizerDesugaringEventConsumer { private final ConcreteArtProfileCollectionAdditions additionsCollection; + private final ArtProfileOptions options; private final CfClassSynthesizerDesugaringEventConsumer parent; private ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer( ConcreteArtProfileCollectionAdditions additionsCollection, + ArtProfileOptions options, CfClassSynthesizerDesugaringEventConsumer parent) { this.additionsCollection = additionsCollection; + this.options = options; this.parent = parent; } public static CfClassSynthesizerDesugaringEventConsumer attach( - ArtProfileCollectionAdditions artProfileCollectionAdditions, - CfClassSynthesizerDesugaringEventConsumer eventConsumer) { + AppView<?> appView, CfClassSynthesizerDesugaringEventConsumer eventConsumer) { + return attach(appView, eventConsumer, ArtProfileCollectionAdditions.create(appView)); + } + + public static CfClassSynthesizerDesugaringEventConsumer attach( + AppView<?> appView, + CfClassSynthesizerDesugaringEventConsumer eventConsumer, + ArtProfileCollectionAdditions artProfileCollectionAdditions) { if (artProfileCollectionAdditions.isNop()) { return eventConsumer; } return new ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer( - artProfileCollectionAdditions.asConcrete(), eventConsumer); + artProfileCollectionAdditions.asConcrete(), + appView.options().getArtProfileOptions(), + eventConsumer); } @Override - public void acceptCollectionConversion(ProgramMethod arrayConversion) { - parent.acceptCollectionConversion(arrayConversion); + public void acceptCollectionConversion(ProgramMethod arrayConversion, ProgramMethod context) { + additionsCollection.addMethodAndHolderIfContextIsInProfile(arrayConversion, context); + parent.acceptCollectionConversion(arrayConversion, context); } @Override @@ -79,8 +97,21 @@ } @Override - public void acceptVarHandleDesugaringClass(DexProgramClass varHandleClass) { - parent.acceptVarHandleDesugaringClass(varHandleClass); + public void acceptVarHandleDesugaringClass(DexProgramClass clazz) { + parent.acceptVarHandleDesugaringClass(clazz); + } + + @Override + public void acceptVarHandleDesugaringClassContext( + DexProgramClass clazz, ProgramDefinition context) { + handleVarHandleDesugaringClassContext(clazz, context, additionsCollection, options); + parent.acceptVarHandleDesugaringClassContext(clazz, context); + } + + @Override + public void finished(AppView<? extends AppInfoWithClassHierarchy> appView) { + additionsCollection.commit(appView); + parent.finished(appView); } @Override
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfInstructionDesugaringEventConsumer.java index 811b1f5..3ac8a23 100644 --- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfInstructionDesugaringEventConsumer.java +++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfInstructionDesugaringEventConsumer.java
@@ -4,10 +4,14 @@ package com.android.tools.r8.profile.art.rewriting; +import static com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingVarHandleDesugaringEventConsumerUtils.handleVarHandleDesugaringClassContext; + import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexClasspathClass; +import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.ProgramDefinition; import com.android.tools.r8.graph.ProgramField; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer; @@ -15,6 +19,7 @@ import com.android.tools.r8.ir.desugar.LambdaClass.Target; import com.android.tools.r8.ir.desugar.constantdynamic.ConstantDynamicClass; import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialBridgeInfo; +import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaringEventConsumer; import java.util.List; public class ArtProfileRewritingCfInstructionDesugaringEventConsumer @@ -24,6 +29,8 @@ private final ConcreteArtProfileCollectionAdditions additionsCollection; private final CfInstructionDesugaringEventConsumer parent; + private final NestBasedAccessDesugaringEventConsumer nestBasedAccessDesugaringEventConsumer; + private ArtProfileRewritingCfInstructionDesugaringEventConsumer( AppView<?> appView, ConcreteArtProfileCollectionAdditions additionsCollection, @@ -31,6 +38,9 @@ this.appView = appView; this.additionsCollection = additionsCollection; this.parent = parent; + this.nestBasedAccessDesugaringEventConsumer = + ArtProfileRewritingNestBasedAccessDesugaringEventConsumer.attach( + additionsCollection, NestBasedAccessDesugaringEventConsumer.empty()); } public static CfInstructionDesugaringEventConsumer attach( @@ -45,21 +55,27 @@ } @Override - public void acceptAPIConversion(ProgramMethod method) { - parent.acceptAPIConversion(method); + public void acceptAPIConversionOutline(ProgramMethod method, ProgramMethod context) { + additionsCollection.addMethodAndHolderIfContextIsInProfile(method, context); + parent.acceptAPIConversionOutline(method, context); } @Override public void acceptBackportedClass(DexProgramClass backportedClass, ProgramMethod context) { + if (appView.options().getArtProfileOptions().isIncludingBackportedClasses()) { + additionsCollection.applyIfContextIsInProfile( + context, + additionsBuilder -> { + additionsBuilder.addRule(backportedClass); + backportedClass.forEachProgramMethod(additionsBuilder::addRule); + }); + } parent.acceptBackportedClass(backportedClass, context); } @Override public void acceptBackportedMethod(ProgramMethod backportedMethod, ProgramMethod context) { - additionsCollection.applyIfContextIsInProfile( - context, - additionsBuilder -> - additionsBuilder.addRule(backportedMethod).addRule(backportedMethod.getHolder())); + additionsCollection.addMethodAndHolderIfContextIsInProfile(backportedMethod, context); parent.acceptBackportedMethod(backportedMethod, context); } @@ -69,8 +85,9 @@ } @Override - public void acceptCollectionConversion(ProgramMethod arrayConversion) { - parent.acceptCollectionConversion(arrayConversion); + public void acceptCollectionConversion(ProgramMethod arrayConversion, ProgramMethod context) { + additionsCollection.addMethodAndHolderIfContextIsInProfile(arrayConversion, context); + parent.acceptCollectionConversion(arrayConversion, context); } @Override @@ -79,21 +96,46 @@ } @Override - public void acceptConstantDynamicClass(ConstantDynamicClass lambdaClass, ProgramMethod context) { - parent.acceptConstantDynamicClass(lambdaClass, context); + public void acceptConstantDynamicClass( + ConstantDynamicClass constantDynamicClass, ProgramMethod context) { + if (appView.options().getArtProfileOptions().isIncludingConstantDynamicClass()) { + additionsCollection.applyIfContextIsInProfile( + context, + additionsBuilder -> { + DexProgramClass clazz = constantDynamicClass.getConstantDynamicProgramClass(); + additionsBuilder.addRule(clazz); + clazz.forEachProgramMethod(additionsBuilder::addRule); + }); + } + parent.acceptConstantDynamicClass(constantDynamicClass, context); } @Override - public void acceptCovariantRetargetMethod(ProgramMethod method) { - parent.acceptCovariantRetargetMethod(method); + public void acceptConstantDynamicRewrittenBootstrapMethod( + ProgramMethod bootstrapMethod, DexMethod oldSignature) { + additionsCollection.applyIfContextIsInProfile( + oldSignature, + additionsBuilder -> + additionsBuilder + .addRule(bootstrapMethod) + .removeMovedMethodRule(oldSignature, bootstrapMethod)); + parent.acceptConstantDynamicRewrittenBootstrapMethod(bootstrapMethod, oldSignature); + } + + @Override + public void acceptCovariantRetargetMethod(ProgramMethod method, ProgramMethod context) { + additionsCollection.addMethodAndHolderIfContextIsInProfile(method, context); + parent.acceptCovariantRetargetMethod(method, context); } @Override public void acceptDefaultAsCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) { additionsCollection.applyIfContextIsInProfile( method, - additionsBuilder -> - additionsBuilder.addRule(companionMethod).addRule(companionMethod.getHolder())); + additionsBuilder -> { + additionsBuilder.addRule(companionMethod).addRule(companionMethod.getHolder()); + companionMethod.getHolder().acceptProgramClassInitializer(additionsBuilder::addRule); + }); parent.acceptDefaultAsCompanionMethod(method, companionMethod); } @@ -114,15 +156,15 @@ @Override public void acceptInvokeSpecialBridgeInfo(InvokeSpecialBridgeInfo info) { - additionsCollection.applyIfContextIsInProfile( - info.getVirtualMethod(), - additionsBuilder -> additionsBuilder.addRule(info.getNewDirectMethod())); + additionsCollection.addMethodIfContextIsInProfile( + info.getNewDirectMethod(), info.getVirtualMethod()); parent.acceptInvokeSpecialBridgeInfo(info); } @Override public void acceptInvokeStaticInterfaceOutliningMethod( ProgramMethod method, ProgramMethod context) { + additionsCollection.addMethodAndHolderIfContextIsInProfile(method, context); parent.acceptInvokeStaticInterfaceOutliningMethod(method, context); } @@ -144,20 +186,27 @@ additionsBuilder.addRule(lambdaProgramClass.getProgramClassInitializer()); } lambdaProgramClass.forEachProgramInstanceInitializer(additionsBuilder::addRule); + if (appView.options().testing.alwaysGenerateLambdaFactoryMethods) { + lambdaProgramClass.forEachProgramStaticMethod(additionsBuilder::addRule); + } }); } private void addLambdaVirtualMethodsIfLambdaImplementationIsInProfile( LambdaClass lambdaClass, ProgramMethod context) { + Target target = lambdaClass.getTarget(); if (shouldConservativelyAddLambdaVirtualMethodsIfLambdaInstantiated(lambdaClass, context)) { additionsCollection.applyIfContextIsInProfile( context, - additionsBuilder -> - lambdaClass - .getLambdaProgramClass() - .forEachProgramVirtualMethod(additionsBuilder::addRule)); + additionsBuilder -> { + lambdaClass + .getLambdaProgramClass() + .forEachProgramVirtualMethod(additionsBuilder::addRule); + if (target.getCallTarget() != target.getImplementationMethod()) { + additionsBuilder.addRule(target.getCallTarget()); + } + }); } else { - Target target = lambdaClass.getTarget(); additionsCollection.applyIfContextIsInProfile( target.getImplementationMethod(), additionsBuilder -> { @@ -187,7 +236,7 @@ .appInfoWithClassHierarchy() .resolveMethod(target.getImplementationMethod(), target.isInterface()) .getResolutionPair(); - if (resolutionResult == null || resolutionResult.isProgramMethod()) { + if (resolutionResult != null && resolutionResult.isProgramMethod()) { // Direct call to other method in the app. Only add virtual methods if the callee is in the // profile. return false; @@ -207,41 +256,35 @@ ProgramMethod bridge, DexProgramClass argumentClass, DexClassAndMethod context) { - additionsCollection.applyIfContextIsInProfile( - context, additionsBuilder -> additionsBuilder.addRule(argumentClass).addRule(bridge)); + nestBasedAccessDesugaringEventConsumer.acceptNestConstructorBridge( + target, bridge, argumentClass, context); parent.acceptNestConstructorBridge(target, bridge, argumentClass, context); } @Override public void acceptNestFieldGetBridge( ProgramField target, ProgramMethod bridge, DexClassAndMethod context) { - additionsCollection.applyIfContextIsInProfile( - context, additionsBuilder -> additionsBuilder.addRule(bridge)); + nestBasedAccessDesugaringEventConsumer.acceptNestFieldGetBridge(target, bridge, context); parent.acceptNestFieldGetBridge(target, bridge, context); } @Override public void acceptNestFieldPutBridge( ProgramField target, ProgramMethod bridge, DexClassAndMethod context) { - additionsCollection.applyIfContextIsInProfile( - context, additionsBuilder -> additionsBuilder.addRule(bridge)); + nestBasedAccessDesugaringEventConsumer.acceptNestFieldPutBridge(target, bridge, context); parent.acceptNestFieldPutBridge(target, bridge, context); } @Override public void acceptNestMethodBridge( ProgramMethod target, ProgramMethod bridge, DexClassAndMethod context) { - additionsCollection.applyIfContextIsInProfile( - context, additionsBuilder -> additionsBuilder.addRule(bridge)); + nestBasedAccessDesugaringEventConsumer.acceptNestMethodBridge(target, bridge, context); parent.acceptNestMethodBridge(target, bridge, context); } @Override public void acceptOutlinedMethod(ProgramMethod outlinedMethod, ProgramMethod context) { - additionsCollection.applyIfContextIsInProfile( - context, - additionsBuilder -> - additionsBuilder.addRule(outlinedMethod).addRule(outlinedMethod.getHolder())); + additionsCollection.addMethodAndHolderIfContextIsInProfile(outlinedMethod, context); parent.acceptOutlinedMethod(outlinedMethod, context); } @@ -249,11 +292,13 @@ public void acceptPrivateAsCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) { additionsCollection.applyIfContextIsInProfile( method, - additionsBuilder -> - additionsBuilder - .addRule(companionMethod) - .addRule(companionMethod.getHolder()) - .removeMovedMethodRule(method, companionMethod)); + additionsBuilder -> { + additionsBuilder + .addRule(companionMethod) + .addRule(companionMethod.getHolder()) + .removeMovedMethodRule(method, companionMethod); + companionMethod.getHolder().acceptProgramClassInitializer(additionsBuilder::addRule); + }); parent.acceptPrivateAsCompanionMethod(method, companionMethod); } @@ -269,30 +314,26 @@ @Override public void acceptRecordEqualsHelperMethod(ProgramMethod method, ProgramMethod context) { - additionsCollection.applyIfContextIsInProfile( - context, additionsBuilder -> additionsBuilder.addRule(method)); + additionsCollection.addMethodIfContextIsInProfile(method, context); parent.acceptRecordEqualsHelperMethod(method, context); } @Override public void acceptRecordGetFieldsAsObjectsHelperMethod( ProgramMethod method, ProgramMethod context) { - additionsCollection.applyIfContextIsInProfile( - context, additionsBuilder -> additionsBuilder.addRule(method)); + additionsCollection.addMethodIfContextIsInProfile(method, context); parent.acceptRecordGetFieldsAsObjectsHelperMethod(method, context); } @Override public void acceptRecordHashCodeHelperMethod(ProgramMethod method, ProgramMethod context) { - additionsCollection.applyIfContextIsInProfile( - context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder())); + additionsCollection.addMethodAndHolderIfContextIsInProfile(method, context); parent.acceptRecordHashCodeHelperMethod(method, context); } @Override public void acceptRecordToStringHelperMethod(ProgramMethod method, ProgramMethod context) { - additionsCollection.applyIfContextIsInProfile( - context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder())); + additionsCollection.addMethodAndHolderIfContextIsInProfile(method, context); parent.acceptRecordToStringHelperMethod(method, context); } @@ -300,60 +341,74 @@ public void acceptStaticAsCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) { additionsCollection.applyIfContextIsInProfile( method, - additionsBuilder -> - additionsBuilder - .addRule(companionMethod) - .addRule(companionMethod.getHolder()) - .removeMovedMethodRule(method, companionMethod)); + additionsBuilder -> { + additionsBuilder + .addRule(companionMethod) + .addRule(companionMethod.getHolder()) + .removeMovedMethodRule(method, companionMethod); + companionMethod.getHolder().acceptProgramClassInitializer(additionsBuilder::addRule); + }); parent.acceptStaticAsCompanionMethod(method, companionMethod); } @Override public void acceptTwrCloseResourceMethod(ProgramMethod closeMethod, ProgramMethod context) { - additionsCollection.applyIfContextIsInProfile( - context, - additionsBuilder -> additionsBuilder.addRule(closeMethod).addRule(closeMethod.getHolder())); + additionsCollection.addMethodAndHolderIfContextIsInProfile(closeMethod, context); parent.acceptTwrCloseResourceMethod(closeMethod, context); } @Override public void acceptUtilityToStringIfNotNullMethod(ProgramMethod method, ProgramMethod context) { + additionsCollection.addMethodAndHolderIfContextIsInProfile(context, method); parent.acceptUtilityToStringIfNotNullMethod(method, context); } @Override public void acceptUtilityThrowClassCastExceptionIfNotNullMethod( ProgramMethod method, ProgramMethod context) { + additionsCollection.addMethodAndHolderIfContextIsInProfile(method, context); parent.acceptUtilityThrowClassCastExceptionIfNotNullMethod(method, context); } @Override public void acceptUtilityThrowIllegalAccessErrorMethod( ProgramMethod method, ProgramMethod context) { + additionsCollection.addMethodAndHolderIfContextIsInProfile(method, context); parent.acceptUtilityThrowIllegalAccessErrorMethod(method, context); } @Override public void acceptUtilityThrowIncompatibleClassChangeErrorMethod( ProgramMethod method, ProgramMethod context) { + additionsCollection.addMethodAndHolderIfContextIsInProfile(method, context); parent.acceptUtilityThrowIncompatibleClassChangeErrorMethod(method, context); } @Override public void acceptUtilityThrowNoSuchMethodErrorMethod( ProgramMethod method, ProgramMethod context) { + additionsCollection.addMethodAndHolderIfContextIsInProfile(method, context); parent.acceptUtilityThrowNoSuchMethodErrorMethod(method, context); } @Override public void acceptUtilityThrowRuntimeExceptionWithMessageMethod( ProgramMethod method, ProgramMethod context) { + additionsCollection.addMethodAndHolderIfContextIsInProfile(method, context); parent.acceptUtilityThrowRuntimeExceptionWithMessageMethod(method, context); } @Override - public void acceptVarHandleDesugaringClass(DexProgramClass varHandleClass) { - parent.acceptVarHandleDesugaringClass(varHandleClass); + public void acceptVarHandleDesugaringClass(DexProgramClass clazz) { + parent.acceptVarHandleDesugaringClass(clazz); + } + + @Override + public void acceptVarHandleDesugaringClassContext( + DexProgramClass clazz, ProgramDefinition context) { + handleVarHandleDesugaringClassContext( + clazz, context, additionsCollection, appView.options().getArtProfileOptions()); + parent.acceptVarHandleDesugaringClassContext(clazz, context); } @Override
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfPostProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfPostProcessingDesugaringEventConsumer.java index 6843852..568efcc 100644 --- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfPostProcessingDesugaringEventConsumer.java +++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfPostProcessingDesugaringEventConsumer.java
@@ -4,15 +4,22 @@ package com.android.tools.r8.profile.art.rewriting; +import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer; + +import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexClasspathClass; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.MethodResolutionResult.FailedResolutionResult; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer; +import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor; import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper; +import com.android.tools.r8.profile.art.ArtProfileOptions; +import com.android.tools.r8.utils.BooleanBox; import java.util.Set; import java.util.concurrent.ExecutionException; @@ -20,38 +27,48 @@ extends CfPostProcessingDesugaringEventConsumer { private final ConcreteArtProfileCollectionAdditions additionsCollection; + private final ArtProfileOptions options; private final CfPostProcessingDesugaringEventConsumer parent; private ArtProfileRewritingCfPostProcessingDesugaringEventConsumer( ConcreteArtProfileCollectionAdditions additionsCollection, + ArtProfileOptions options, CfPostProcessingDesugaringEventConsumer parent) { this.additionsCollection = additionsCollection; + this.options = options; this.parent = parent; } public static CfPostProcessingDesugaringEventConsumer attach( + AppView<?> appView, ArtProfileCollectionAdditions artProfileCollectionAdditions, CfPostProcessingDesugaringEventConsumer eventConsumer) { if (artProfileCollectionAdditions.isNop()) { return eventConsumer; } return new ArtProfileRewritingCfPostProcessingDesugaringEventConsumer( - artProfileCollectionAdditions.asConcrete(), eventConsumer); + artProfileCollectionAdditions.asConcrete(), + appView.options().getArtProfileOptions(), + eventConsumer); } @Override - public void acceptAPIConversionCallback(ProgramMethod method) { - parent.acceptAPIConversionCallback(method); + public void acceptAPIConversionCallback( + ProgramMethod callbackMethod, ProgramMethod convertedMethod) { + additionsCollection.addMethodIfContextIsInProfile(callbackMethod, convertedMethod); + parent.acceptAPIConversionCallback(callbackMethod, convertedMethod); } @Override - public void acceptCollectionConversion(ProgramMethod arrayConversion) { - parent.acceptCollectionConversion(arrayConversion); + public void acceptCollectionConversion(ProgramMethod arrayConversion, ProgramMethod context) { + additionsCollection.addMethodAndHolderIfContextIsInProfile(arrayConversion, context); + parent.acceptCollectionConversion(arrayConversion, context); } @Override - public void acceptCovariantRetargetMethod(ProgramMethod method) { - parent.acceptCovariantRetargetMethod(method); + public void acceptCovariantRetargetMethod(ProgramMethod method, ProgramMethod context) { + additionsCollection.addMethodAndHolderIfContextIsInProfile(context, method); + parent.acceptCovariantRetargetMethod(method, context); } @Override @@ -60,8 +77,12 @@ } @Override - public void acceptDesugaredLibraryRetargeterForwardingMethod(ProgramMethod method) { - parent.acceptDesugaredLibraryRetargeterForwardingMethod(method); + public void acceptDesugaredLibraryRetargeterForwardingMethod( + ProgramMethod method, EmulatedDispatchMethodDescriptor descriptor) { + if (options.isIncludingDesugaredLibraryRetargeterForwardingMethodsUnconditionally()) { + additionsCollection.apply(additions -> additions.addMethodRule(method, emptyConsumer())); + } + parent.acceptDesugaredLibraryRetargeterForwardingMethod(method, descriptor); } @Override @@ -88,14 +109,29 @@ @Override public void acceptInterfaceMethodDesugaringForwardingMethod( ProgramMethod method, DexClassAndMethod baseMethod) { - additionsCollection.applyIfContextIsInProfile( - baseMethod, additionsBuilder -> additionsBuilder.addRule(method)); + additionsCollection.addMethodIfContextIsInProfile(method, baseMethod, emptyConsumer()); parent.acceptInterfaceMethodDesugaringForwardingMethod(method, baseMethod); } @Override - public void acceptThrowingMethod(ProgramMethod method, DexType errorType) { - parent.acceptThrowingMethod(method, errorType); + public void acceptThrowingMethod( + ProgramMethod method, DexType errorType, FailedResolutionResult resolutionResult) { + if (options.isIncludingThrowingMethods()) { + BooleanBox seenMethodCausingError = new BooleanBox(); + resolutionResult.forEachFailureDependency( + emptyConsumer(), + methodCausingError -> { + additionsCollection.applyIfContextIsInProfile( + methodCausingError.getReference(), + additionsBuilder -> additionsBuilder.addRule(method)); + seenMethodCausingError.set(); + }); + if (seenMethodCausingError.isFalse()) { + additionsCollection.applyIfContextIsInProfile( + method.getHolder(), additions -> additions.addMethodRule(method, emptyConsumer())); + } + } + parent.acceptThrowingMethod(method, errorType, resolutionResult); } @Override
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer.java new file mode 100644 index 0000000..9722924 --- /dev/null +++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer.java
@@ -0,0 +1,38 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.profile.art.rewriting; + +import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.ir.desugar.CovariantReturnTypeAnnotationTransformerEventConsumer; + +public class ArtProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer + implements CovariantReturnTypeAnnotationTransformerEventConsumer { + + private final ConcreteArtProfileCollectionAdditions additionsCollection; + private final CovariantReturnTypeAnnotationTransformerEventConsumer parent; + + private ArtProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer( + ConcreteArtProfileCollectionAdditions additionsCollection, + CovariantReturnTypeAnnotationTransformerEventConsumer parent) { + this.additionsCollection = additionsCollection; + this.parent = parent; + } + + public static CovariantReturnTypeAnnotationTransformerEventConsumer attach( + ArtProfileCollectionAdditions additionsCollection, + CovariantReturnTypeAnnotationTransformerEventConsumer eventConsumer) { + if (additionsCollection.isNop()) { + return eventConsumer; + } + return new ArtProfileRewritingCovariantReturnTypeAnnotationTransformerEventConsumer( + additionsCollection.asConcrete(), eventConsumer); + } + + @Override + public void acceptCovariantReturnTypeBridgeMethod(ProgramMethod bridge, ProgramMethod target) { + additionsCollection.addMethodIfContextIsInProfile(bridge, target); + parent.acceptCovariantReturnTypeBridgeMethod(bridge, target); + } +}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingMemberRebindingEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingMemberRebindingEventConsumer.java new file mode 100644 index 0000000..df87170 --- /dev/null +++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingMemberRebindingEventConsumer.java
@@ -0,0 +1,53 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.profile.art.rewriting; + +import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer; + +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexClassAndMethod; +import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.optimize.MemberRebindingEventConsumer; +import com.android.tools.r8.optimize.MemberRebindingLens; +import com.android.tools.r8.shaking.AppInfoWithLiveness; + +public class ArtProfileRewritingMemberRebindingEventConsumer + implements MemberRebindingEventConsumer { + + private final ConcreteArtProfileCollectionAdditions additionsCollection; + private final MemberRebindingEventConsumer parent; + + private ArtProfileRewritingMemberRebindingEventConsumer( + ConcreteArtProfileCollectionAdditions additionsCollection, + MemberRebindingEventConsumer parent) { + this.additionsCollection = additionsCollection; + this.parent = parent; + } + + public static MemberRebindingEventConsumer attach( + AppView<AppInfoWithLiveness> appView, MemberRebindingEventConsumer eventConsumer) { + ArtProfileCollectionAdditions additionsCollection = + ArtProfileCollectionAdditions.create(appView); + if (additionsCollection.isNop()) { + return eventConsumer; + } + return new ArtProfileRewritingMemberRebindingEventConsumer( + additionsCollection.asConcrete(), eventConsumer); + } + + @Override + public void acceptMemberRebindingBridgeMethod( + ProgramMethod bridgeMethod, DexClassAndMethod targetMethod) { + additionsCollection.addMethodIfContextIsInProfile(bridgeMethod, targetMethod, emptyConsumer()); + parent.acceptMemberRebindingBridgeMethod(bridgeMethod, targetMethod); + } + + @Override + public void finished( + AppView<AppInfoWithLiveness> appView, MemberRebindingLens memberRebindingLens) { + additionsCollection.commit(appView); + parent.finished(appView, memberRebindingLens); + } +}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingMethodProcessorEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingMethodProcessorEventConsumer.java index 325422e..dea3841 100644 --- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingMethodProcessorEventConsumer.java +++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingMethodProcessorEventConsumer.java
@@ -4,8 +4,10 @@ package com.android.tools.r8.profile.art.rewriting; +import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer; +import com.android.tools.r8.shaking.AppInfoWithLiveness; public class ArtProfileRewritingMethodProcessorEventConsumer extends MethodProcessorEventConsumer { @@ -20,6 +22,17 @@ } public static MethodProcessorEventConsumer attach( + AppView<?> appView, MethodProcessorEventConsumer eventConsumer) { + ArtProfileCollectionAdditions additionsCollection = + ArtProfileCollectionAdditions.create(appView); + if (additionsCollection.isNop()) { + return eventConsumer; + } + return new ArtProfileRewritingMethodProcessorEventConsumer( + additionsCollection.asConcrete(), eventConsumer); + } + + public static MethodProcessorEventConsumer attach( ArtProfileCollectionAdditions artProfileCollectionAdditions, MethodProcessorEventConsumer eventConsumer) { if (artProfileCollectionAdditions.isNop()) { @@ -30,9 +43,15 @@ } @Override + public void acceptAssertionErrorCreateMethod(ProgramMethod method, ProgramMethod context) { + additionsCollection.addMethodAndHolderIfContextIsInProfile(method, context); + parent.acceptAssertionErrorCreateMethod(method, context); + } + + @Override public void acceptEnumUnboxerCheckNotZeroContext(ProgramMethod method, ProgramMethod context) { additionsCollection.applyIfContextIsInProfile( - context, additionsBuilder -> additionsBuilder.addRule(method)); + context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder())); parent.acceptEnumUnboxerCheckNotZeroContext(method, context); } @@ -64,6 +83,12 @@ } @Override + public void acceptServiceLoaderLoadUtilityMethod(ProgramMethod method, ProgramMethod context) { + additionsCollection.addMethodAndHolderIfContextIsInProfile(method, context); + parent.acceptServiceLoaderLoadUtilityMethod(method, context); + } + + @Override public void acceptUtilityToStringIfNotNullMethod(ProgramMethod method, ProgramMethod context) { additionsCollection.applyIfContextIsInProfile( context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder())); @@ -109,4 +134,10 @@ context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder())); parent.acceptUtilityThrowRuntimeExceptionWithMessageMethod(method, context); } + + @Override + public void finished(AppView<AppInfoWithLiveness> appView) { + additionsCollection.commit(appView); + parent.finished(appView); + } }
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingNestBasedAccessDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingNestBasedAccessDesugaringEventConsumer.java new file mode 100644 index 0000000..2988ce4 --- /dev/null +++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingNestBasedAccessDesugaringEventConsumer.java
@@ -0,0 +1,76 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.profile.art.rewriting; + +import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer; + +import com.android.tools.r8.graph.DexClassAndMethod; +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.ProgramField; +import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaringEventConsumer; + +public class ArtProfileRewritingNestBasedAccessDesugaringEventConsumer + implements NestBasedAccessDesugaringEventConsumer { + + private final ConcreteArtProfileCollectionAdditions additionsCollection; + private final NestBasedAccessDesugaringEventConsumer parent; + + private ArtProfileRewritingNestBasedAccessDesugaringEventConsumer( + ConcreteArtProfileCollectionAdditions additionsCollection, + NestBasedAccessDesugaringEventConsumer parent) { + this.additionsCollection = additionsCollection; + this.parent = parent; + } + + public static NestBasedAccessDesugaringEventConsumer attach( + ArtProfileCollectionAdditions additionsCollection, + NestBasedAccessDesugaringEventConsumer eventConsumer) { + if (additionsCollection.isNop()) { + return eventConsumer; + } + return new ArtProfileRewritingNestBasedAccessDesugaringEventConsumer( + additionsCollection.asConcrete(), eventConsumer); + } + + @Override + public void acceptNestConstructorBridge( + ProgramMethod target, + ProgramMethod bridge, + DexProgramClass argumentClass, + DexClassAndMethod context) { + if (context.isProgramMethod()) { + additionsCollection.applyIfContextIsInProfile( + context.asProgramMethod(), + additionsBuilder -> additionsBuilder.addRule(argumentClass).addRule(bridge)); + } else { + additionsCollection.apply( + additions -> + additions.addClassRule(argumentClass).addMethodRule(bridge, emptyConsumer())); + } + parent.acceptNestConstructorBridge(target, bridge, argumentClass, context); + } + + @Override + public void acceptNestFieldGetBridge( + ProgramField target, ProgramMethod bridge, DexClassAndMethod context) { + additionsCollection.addMethodIfContextIsInProfile(bridge, context, emptyConsumer()); + parent.acceptNestFieldGetBridge(target, bridge, context); + } + + @Override + public void acceptNestFieldPutBridge( + ProgramField target, ProgramMethod bridge, DexClassAndMethod context) { + additionsCollection.addMethodIfContextIsInProfile(bridge, context, emptyConsumer()); + parent.acceptNestFieldPutBridge(target, bridge, context); + } + + @Override + public void acceptNestMethodBridge( + ProgramMethod target, ProgramMethod bridge, DexClassAndMethod context) { + additionsCollection.addMethodIfContextIsInProfile(bridge, context, emptyConsumer()); + parent.acceptNestMethodBridge(target, bridge, context); + } +}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingOutlineOptimizationEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingOutlineOptimizationEventConsumer.java new file mode 100644 index 0000000..2184a87 --- /dev/null +++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingOutlineOptimizationEventConsumer.java
@@ -0,0 +1,52 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.profile.art.rewriting; + +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.ir.optimize.outliner.OutlineOptimizationEventConsumer; +import com.android.tools.r8.shaking.AppInfoWithLiveness; +import java.util.Collection; + +public class ArtProfileRewritingOutlineOptimizationEventConsumer + implements OutlineOptimizationEventConsumer { + + private final ConcreteArtProfileCollectionAdditions additionsCollection; + private final OutlineOptimizationEventConsumer parent; + + private ArtProfileRewritingOutlineOptimizationEventConsumer( + ConcreteArtProfileCollectionAdditions additionsCollection, + OutlineOptimizationEventConsumer parent) { + this.additionsCollection = additionsCollection; + this.parent = parent; + } + + public static OutlineOptimizationEventConsumer attach( + AppView<AppInfoWithLiveness> appView, OutlineOptimizationEventConsumer eventConsumer) { + ArtProfileCollectionAdditions additionsCollection = + ArtProfileCollectionAdditions.create(appView); + if (additionsCollection.isNop()) { + return eventConsumer; + } + return new ArtProfileRewritingOutlineOptimizationEventConsumer( + additionsCollection.asConcrete(), eventConsumer); + } + + @Override + public void acceptOutlineMethod(ProgramMethod method, Collection<ProgramMethod> contexts) { + for (ProgramMethod context : contexts) { + additionsCollection.applyIfContextIsInProfile( + context, + additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder())); + } + parent.acceptOutlineMethod(method, contexts); + } + + @Override + public void finished(AppView<AppInfoWithLiveness> appView) { + additionsCollection.commit(appView); + parent.finished(appView); + } +}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingRootSetBuilderEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingRootSetBuilderEventConsumer.java new file mode 100644 index 0000000..005c33c --- /dev/null +++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingRootSetBuilderEventConsumer.java
@@ -0,0 +1,66 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.profile.art.rewriting; + +import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.shaking.RootSetBuilderEventConsumer; + +public class ArtProfileRewritingRootSetBuilderEventConsumer implements RootSetBuilderEventConsumer { + + private final ConcreteArtProfileCollectionAdditions additionsCollection; + private final RootSetBuilderEventConsumer parent; + + private ArtProfileRewritingRootSetBuilderEventConsumer( + ConcreteArtProfileCollectionAdditions additionsCollection, + RootSetBuilderEventConsumer parent) { + this.additionsCollection = additionsCollection; + this.parent = parent; + } + + public static RootSetBuilderEventConsumer attach( + ArtProfileCollectionAdditions additionsCollection, + RootSetBuilderEventConsumer eventConsumer) { + if (additionsCollection.isNop()) { + return eventConsumer; + } + return new ArtProfileRewritingRootSetBuilderEventConsumer( + additionsCollection.asConcrete(), eventConsumer); + } + + @Override + public void acceptCompanionClassClinit(ProgramMethod method) { + parent.acceptCompanionClassClinit(method); + } + + @Override + public void acceptDefaultAsCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) { + additionsCollection.addMethodAndHolderIfContextIsInProfile(companionMethod, method); + parent.acceptDefaultAsCompanionMethod(method, companionMethod); + } + + @Override + public void acceptPrivateAsCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) { + additionsCollection.applyIfContextIsInProfile( + method, + additionsBuilder -> + additionsBuilder + .addRule(companionMethod) + .addRule(companionMethod.getHolder()) + .removeMovedMethodRule(method, companionMethod)); + parent.acceptPrivateAsCompanionMethod(method, companionMethod); + } + + @Override + public void acceptStaticAsCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) { + additionsCollection.applyIfContextIsInProfile( + method, + additionsBuilder -> + additionsBuilder + .addRule(companionMethod) + .addRule(companionMethod.getHolder()) + .removeMovedMethodRule(method, companionMethod)); + parent.acceptStaticAsCompanionMethod(method, companionMethod); + } +}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingVarHandleDesugaringEventConsumerUtils.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingVarHandleDesugaringEventConsumerUtils.java new file mode 100644 index 0000000..f53e9bc --- /dev/null +++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingVarHandleDesugaringEventConsumerUtils.java
@@ -0,0 +1,34 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.profile.art.rewriting; + +import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer; + +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.ProgramDefinition; +import com.android.tools.r8.profile.art.ArtProfileOptions; + +public class ArtProfileRewritingVarHandleDesugaringEventConsumerUtils { + + static void handleVarHandleDesugaringClassContext( + DexProgramClass varHandleClass, + ProgramDefinition context, + ConcreteArtProfileCollectionAdditions additionsCollection, + ArtProfileOptions options) { + if (options.isIncludingVarHandleClasses()) { + additionsCollection.applyIfContextIsInProfile( + context, + additions -> { + additions.addClassRule(varHandleClass); + varHandleClass.forEachProgramMethod( + method -> additions.addMethodRule(method, emptyConsumer())); + }, + additionsBuilder -> { + additionsBuilder.addRule(varHandleClass); + varHandleClass.forEachProgramMethod(additionsBuilder::addRule); + }); + } + } +}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ConcreteArtProfileCollectionAdditions.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ConcreteArtProfileCollectionAdditions.java index 85c54e1..1d1a897 100644 --- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ConcreteArtProfileCollectionAdditions.java +++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ConcreteArtProfileCollectionAdditions.java
@@ -5,12 +5,15 @@ package com.android.tools.r8.profile.art.rewriting; import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramDefinition; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.profile.art.ArtProfile; import com.android.tools.r8.profile.art.ArtProfileCollection; +import com.android.tools.r8.profile.art.ArtProfileMethodRuleInfoImpl; import com.android.tools.r8.profile.art.NonEmptyArtProfileCollection; import com.android.tools.r8.profile.art.rewriting.ArtProfileAdditions.ArtProfileAdditionsBuilder; import com.google.common.collect.Iterables; @@ -24,6 +27,8 @@ private final List<ArtProfileAdditions> additionsCollection; + private boolean committed = false; + private ConcreteArtProfileCollectionAdditions(List<ArtProfileAdditions> additionsCollection) { this.additionsCollection = additionsCollection; } @@ -36,8 +41,50 @@ assert !additionsCollection.isEmpty(); } + @Override + public void addMethodIfContextIsInProfile(ProgramMethod method, ProgramMethod context) { + applyIfContextIsInProfile(context, additionsBuilder -> additionsBuilder.addRule(method)); + } + + public void addMethodIfContextIsInProfile( + ProgramMethod method, + DexClassAndMethod context, + Consumer<ArtProfileMethodRuleInfoImpl.Builder> methodRuleInfoBuilderConsumer) { + if (context.isProgramMethod()) { + applyIfContextIsInProfile( + context.asProgramMethod(), additionsBuilder -> additionsBuilder.addRule(method)); + } else { + apply( + artProfileAdditions -> + artProfileAdditions.addMethodRule(method, methodRuleInfoBuilderConsumer)); + } + } + + public void addMethodAndHolderIfContextIsInProfile(ProgramMethod method, ProgramMethod context) { + applyIfContextIsInProfile( + context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder())); + } + + void apply(Consumer<ArtProfileAdditions> additionsConsumer) { + for (ArtProfileAdditions artProfileAdditions : additionsCollection) { + additionsConsumer.accept(artProfileAdditions); + } + } + void applyIfContextIsInProfile( - DexClass context, Consumer<ArtProfileAdditions> additionsConsumer) { + ProgramDefinition context, + Consumer<ArtProfileAdditions> additionsConsumer, + Consumer<ArtProfileAdditionsBuilder> additionsBuilderConsumer) { + if (context.isProgramClass()) { + applyIfContextIsInProfile(context.asProgramClass(), additionsConsumer); + } else { + assert context.isProgramMethod(); + applyIfContextIsInProfile(context.asProgramMethod(), additionsBuilderConsumer); + } + } + + void applyIfContextIsInProfile( + DexProgramClass context, Consumer<ArtProfileAdditions> additionsConsumer) { applyIfContextIsInProfile(context.getType(), additionsConsumer); } @@ -48,7 +95,7 @@ } public void applyIfContextIsInProfile( - DexClassAndMethod context, Consumer<ArtProfileAdditionsBuilder> builderConsumer) { + ProgramMethod context, Consumer<ArtProfileAdditionsBuilder> builderConsumer) { applyIfContextIsInProfile(context.getReference(), builderConsumer); } @@ -67,9 +114,11 @@ @Override public void commit(AppView<?> appView) { + assert !committed; if (hasAdditions()) { appView.setArtProfileCollection(createNewArtProfileCollection()); } + committed = true; } private ArtProfileCollection createNewArtProfileCollection() { @@ -106,4 +155,10 @@ } return this; } + + @Override + public boolean verifyIsCommitted() { + assert committed; + return true; + } }
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/MethodRuleAdditionConfig.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/MethodRuleAdditionConfig.java deleted file mode 100644 index 5f4f17f..0000000 --- a/src/main/java/com/android/tools/r8/profile/art/rewriting/MethodRuleAdditionConfig.java +++ /dev/null
@@ -1,38 +0,0 @@ -// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -package com.android.tools.r8.profile.art.rewriting; - -import com.android.tools.r8.profile.art.ArtProfileMethodRule; -import com.android.tools.r8.profile.art.ArtProfileMethodRuleInfoImpl; - -public abstract class MethodRuleAdditionConfig { - - public static MethodRuleAdditionConfig getDefault() { - return DefaultMethodRuleAdditionConfig.getInstance(); - } - - public abstract void configureMethodRuleInfo( - ArtProfileMethodRuleInfoImpl.Builder methodRuleInfoBuilder, - ArtProfileMethodRule contextMethodRule); - - private static class DefaultMethodRuleAdditionConfig extends MethodRuleAdditionConfig { - - private static final DefaultMethodRuleAdditionConfig INSTANCE = - new DefaultMethodRuleAdditionConfig(); - - private DefaultMethodRuleAdditionConfig() {} - - static DefaultMethodRuleAdditionConfig getInstance() { - return INSTANCE; - } - - @Override - public void configureMethodRuleInfo( - ArtProfileMethodRuleInfoImpl.Builder methodRuleInfoBuilder, - ArtProfileMethodRule contextMethodRule) { - methodRuleInfoBuilder.joinFlags(contextMethodRule.getMethodRuleInfo()); - } - } -}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/NopArtProfileCollectionAdditions.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/NopArtProfileCollectionAdditions.java index a0c6925..8bfd4a6 100644 --- a/src/main/java/com/android/tools/r8/profile/art/rewriting/NopArtProfileCollectionAdditions.java +++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/NopArtProfileCollectionAdditions.java
@@ -6,6 +6,7 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.profile.art.ArtProfileCollection; import com.android.tools.r8.profile.art.rewriting.ArtProfileAdditions.ArtProfileAdditionsBuilder; import java.util.function.Consumer; @@ -23,6 +24,11 @@ } @Override + public void addMethodIfContextIsInProfile(ProgramMethod method, ProgramMethod context) { + // Intentionally empty. + } + + @Override public void applyIfContextIsInProfile( DexMethod context, Consumer<ArtProfileAdditionsBuilder> builderConsumer) { // Intentionally empty. @@ -51,4 +57,10 @@ // Intentionally empty. return this; } + + @Override + public boolean verifyIsCommitted() { + // Nothing to commit. + return true; + } }
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java index bbdb059..15781741 100644 --- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java +++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -473,6 +473,7 @@ Enqueuer( AppView<? extends AppInfoWithClassHierarchy> appView, + ArtProfileCollectionAdditions artProfileCollectionAdditions, ExecutorService executorService, SubtypingInfo subtypingInfo, GraphConsumer keptGraphConsumer, @@ -481,7 +482,7 @@ InternalOptions options = appView.options(); this.appInfo = appView.appInfo(); this.appView = appView.withClassHierarchy(); - this.artProfileCollectionAdditions = ArtProfileCollectionAdditions.create(appView); + this.artProfileCollectionAdditions = artProfileCollectionAdditions; this.deferredTracing = EnqueuerDeferredTracing.create(appView, this, mode); this.executorService = executorService; this.subtypingInfo = subtypingInfo; @@ -531,6 +532,10 @@ return appView.appInfo(); } + public ArtProfileCollectionAdditions getArtProfileCollectionAdditions() { + return artProfileCollectionAdditions; + } + public Mode getMode() { return mode; } @@ -4010,7 +4015,7 @@ // registered first and no dependencies may exist among them. SyntheticAdditions additions = new SyntheticAdditions(appView.createProcessorContext()); desugar(additions); - synthesizeInterfaceMethodBridges(additions); + synthesizeInterfaceMethodBridges(); if (additions.isEmpty()) { return; } @@ -4164,11 +4169,13 @@ } } - private void synthesizeInterfaceMethodBridges(SyntheticAdditions additions) { - for (ProgramMethod bridge : syntheticInterfaceMethodBridges.values()) { + private void synthesizeInterfaceMethodBridges() { + for (InterfaceMethodSyntheticBridgeAction action : syntheticInterfaceMethodBridges.values()) { + ProgramMethod bridge = action.getMethodToKeep(); DexProgramClass holder = bridge.getHolder(); DexEncodedMethod method = bridge.getDefinition(); holder.addVirtualMethod(method); + artProfileCollectionAdditions.addMethodIfContextIsInProfile(bridge, action.getSingleTarget()); } syntheticInterfaceMethodBridges.clear(); } @@ -4473,7 +4480,7 @@ } } ConsequentRootSetBuilder consequentSetBuilder = - ConsequentRootSet.builder(appView, subtypingInfo, this); + ConsequentRootSet.builder(appView, this, subtypingInfo); IfRuleEvaluator ifRuleEvaluator = new IfRuleEvaluator( appView, @@ -4567,6 +4574,7 @@ CfPostProcessingDesugaringEventConsumer eventConsumer = CfPostProcessingDesugaringEventConsumer.createForR8( + appView, syntheticAdditions, artProfileCollectionAdditions, desugaring, @@ -4633,7 +4641,7 @@ } private ConsequentRootSet computeDelayedInterfaceMethodSyntheticBridges() { - RootSetBuilder builder = RootSet.builder(appView, subtypingInfo); + RootSetBuilder builder = RootSet.builder(appView, this, subtypingInfo); for (DelayedRootSetActionItem delayedRootSetActionItem : rootSet.delayedRootSetActionItems) { if (delayedRootSetActionItem.isInterfaceMethodSyntheticBridgeAction()) { handleInterfaceMethodSyntheticBridgeAction( @@ -4643,8 +4651,8 @@ return builder.buildConsequentRootSet(); } - private final Map<DexMethod, ProgramMethod> syntheticInterfaceMethodBridges = - new LinkedHashMap<>(); + private final Map<DexMethod, InterfaceMethodSyntheticBridgeAction> + syntheticInterfaceMethodBridges = new LinkedHashMap<>(); private void identifySyntheticInterfaceMethodBridges( InterfaceMethodSyntheticBridgeAction action) { @@ -4656,8 +4664,7 @@ if (methodToKeep != singleTarget && !syntheticInterfaceMethodBridges.containsKey( methodToKeep.getDefinition().getReference())) { - syntheticInterfaceMethodBridges.put( - methodToKeep.getDefinition().getReference(), methodToKeep); + syntheticInterfaceMethodBridges.put(methodToKeep.getDefinition().getReference(), action); } }
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java index cfce371..e1d0124 100644 --- a/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java +++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerFactory.java
@@ -9,6 +9,7 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.SubtypingInfo; +import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions; import com.android.tools.r8.shaking.Enqueuer.Mode; import java.util.Set; import java.util.concurrent.ExecutorService; @@ -17,9 +18,16 @@ public static Enqueuer createForInitialTreeShaking( AppView<? extends AppInfoWithClassHierarchy> appView, + ArtProfileCollectionAdditions artProfileCollectionAdditions, ExecutorService executorService, SubtypingInfo subtypingInfo) { - return new Enqueuer(appView, executorService, subtypingInfo, null, Mode.INITIAL_TREE_SHAKING); + return new Enqueuer( + appView, + artProfileCollectionAdditions, + executorService, + subtypingInfo, + null, + Mode.INITIAL_TREE_SHAKING); } public static Enqueuer createForFinalTreeShaking( @@ -28,9 +36,16 @@ SubtypingInfo subtypingInfo, GraphConsumer keptGraphConsumer, Set<DexType> initialPrunedTypes) { + ArtProfileCollectionAdditions artProfileCollectionAdditions = + ArtProfileCollectionAdditions.create(appView); Enqueuer enqueuer = new Enqueuer( - appView, executorService, subtypingInfo, keptGraphConsumer, Mode.FINAL_TREE_SHAKING); + appView, + artProfileCollectionAdditions, + executorService, + subtypingInfo, + keptGraphConsumer, + Mode.FINAL_TREE_SHAKING); appView.withProtoShrinker( shrinker -> enqueuer.setInitialDeadProtoTypes(shrinker.getDeadProtoTypes())); enqueuer.setInitialPrunedTypes(initialPrunedTypes); @@ -41,8 +56,15 @@ AppView<? extends AppInfoWithClassHierarchy> appView, ExecutorService executorService, SubtypingInfo subtypingInfo) { + ArtProfileCollectionAdditions artProfileCollectionAdditions = + ArtProfileCollectionAdditions.create(appView); return new Enqueuer( - appView, executorService, subtypingInfo, null, Mode.INITIAL_MAIN_DEX_TRACING); + appView, + artProfileCollectionAdditions, + executorService, + subtypingInfo, + null, + Mode.INITIAL_MAIN_DEX_TRACING); } public static Enqueuer createForFinalMainDexTracing( @@ -50,8 +72,15 @@ ExecutorService executorService, SubtypingInfo subtypingInfo, GraphConsumer keptGraphConsumer) { + ArtProfileCollectionAdditions artProfileCollectionAdditions = + ArtProfileCollectionAdditions.create(appView); return new Enqueuer( - appView, executorService, subtypingInfo, keptGraphConsumer, Mode.FINAL_MAIN_DEX_TRACING); + appView, + artProfileCollectionAdditions, + executorService, + subtypingInfo, + keptGraphConsumer, + Mode.FINAL_MAIN_DEX_TRACING); } public static Enqueuer createForGenerateMainDexList( @@ -59,8 +88,15 @@ ExecutorService executorService, SubtypingInfo subtypingInfo, GraphConsumer keptGraphConsumer) { + ArtProfileCollectionAdditions artProfileCollectionAdditions = + ArtProfileCollectionAdditions.create(appView); return new Enqueuer( - appView, executorService, subtypingInfo, keptGraphConsumer, Mode.GENERATE_MAIN_DEX_LIST); + appView, + artProfileCollectionAdditions, + executorService, + subtypingInfo, + keptGraphConsumer, + Mode.GENERATE_MAIN_DEX_LIST); } public static Enqueuer createForWhyAreYouKeeping( @@ -68,7 +104,14 @@ ExecutorService executorService, SubtypingInfo subtypingInfo, GraphConsumer keptGraphConsumer) { + ArtProfileCollectionAdditions artProfileCollectionAdditions = + ArtProfileCollectionAdditions.create(appView); return new Enqueuer( - appView, executorService, subtypingInfo, keptGraphConsumer, Mode.WHY_ARE_YOU_KEEPING); + appView, + artProfileCollectionAdditions, + executorService, + subtypingInfo, + keptGraphConsumer, + Mode.WHY_ARE_YOU_KEEPING); } }
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java b/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java index 4cc4154..8baf037 100644 --- a/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java +++ b/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
@@ -6,6 +6,7 @@ import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull; +import com.android.tools.r8.androidapi.AndroidApiLevelCompute; import com.android.tools.r8.dex.IndexedItemCollection; import com.android.tools.r8.graph.AppInfoWithClassHierarchy; import com.android.tools.r8.graph.AppView; @@ -14,6 +15,7 @@ import com.android.tools.r8.graph.DexCallSite; import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexMethodHandle; import com.android.tools.r8.graph.DexProgramClass; @@ -22,6 +24,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.UseRegistry; +import com.android.tools.r8.synthesis.SyntheticItems; import com.android.tools.r8.utils.Box; import java.util.Set; import java.util.function.Consumer; @@ -41,6 +44,9 @@ } public void run(Set<DexType> roots) { + SyntheticItems syntheticItems = appView.getSyntheticItems(); + DexItemFactory factory = appView.dexItemFactory(); + AndroidApiLevelCompute apiLevelCompute = appView.apiLevelCompute(); for (DexType type : roots) { DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(type)); // Should only happen for library classes, which are filtered out. @@ -48,7 +54,7 @@ consumer.accept(type); // Super and interfaces are live, no need to add them. if (!DexAnnotation.hasSynthesizedClassAnnotation( - clazz.annotations(), appView.dexItemFactory(), appView.getSyntheticItems())) { + clazz.annotations(), factory, syntheticItems, apiLevelCompute)) { traceAnnotationsDirectDependencies(clazz.annotations()); } clazz.forEachField(field -> consumer.accept(field.getReference().type));
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilderEventConsumer.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilderEventConsumer.java new file mode 100644 index 0000000..9378b76 --- /dev/null +++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilderEventConsumer.java
@@ -0,0 +1,57 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.shaking; + +import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.ir.desugar.itf.InterfaceMethodDesugaringBaseEventConsumer; +import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions; +import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingRootSetBuilderEventConsumer; + +public interface RootSetBuilderEventConsumer extends InterfaceMethodDesugaringBaseEventConsumer { + + static RootSetBuilderEventConsumer create( + ArtProfileCollectionAdditions artProfileCollectionAdditions) { + return ArtProfileRewritingRootSetBuilderEventConsumer.attach( + artProfileCollectionAdditions, empty()); + } + + static EmptyRootSetBuilderEventConsumer empty() { + return EmptyRootSetBuilderEventConsumer.getInstance(); + } + + class EmptyRootSetBuilderEventConsumer implements RootSetBuilderEventConsumer { + + private static final EmptyRootSetBuilderEventConsumer INSTANCE = + new EmptyRootSetBuilderEventConsumer(); + + private EmptyRootSetBuilderEventConsumer() {} + + static EmptyRootSetBuilderEventConsumer getInstance() { + return INSTANCE; + } + + @Override + public void acceptCompanionClassClinit(ProgramMethod method) { + // Intentionally empty. + } + + @Override + public void acceptDefaultAsCompanionMethod( + ProgramMethod method, ProgramMethod companionMethod) { + // Intentionally empty. + } + + @Override + public void acceptPrivateAsCompanionMethod( + ProgramMethod method, ProgramMethod companionMethod) { + // Intentionally empty. + } + + @Override + public void acceptStaticAsCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) { + // Intentionally empty. + } + } +}
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java index 149201d..b5810f2 100644 --- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java +++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -50,10 +50,10 @@ import com.android.tools.r8.ir.analysis.type.DynamicType; import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper; -import com.android.tools.r8.ir.desugar.itf.InterfaceMethodDesugaringBaseEventConsumer; import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple; import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfo; import com.android.tools.r8.logging.Log; +import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions; import com.android.tools.r8.repackaging.RepackagingUtils; import com.android.tools.r8.shaking.AnnotationMatchResult.AnnotationsIgnoredMatchResult; import com.android.tools.r8.shaking.AnnotationMatchResult.ConcreteAnnotationMatchResult; @@ -113,6 +113,7 @@ private final AppView<? extends AppInfoWithClassHierarchy> appView; private AssumeInfoCollection.Builder assumeInfoCollectionBuilder; + private final RootSetBuilderEventConsumer eventConsumer; private final SubtypingInfo subtypingInfo; private final DirectMappedDexApplication application; private final Iterable<? extends ProguardConfigurationRule> rules; @@ -156,9 +157,11 @@ private RootSetBuilder( AppView<? extends AppInfoWithClassHierarchy> appView, + RootSetBuilderEventConsumer eventConsumer, SubtypingInfo subtypingInfo, Iterable<? extends ProguardConfigurationRule> rules) { this.appView = appView; + this.eventConsumer = eventConsumer; this.subtypingInfo = subtypingInfo; this.application = appView.appInfo().app().asDirect(); this.rules = rules; @@ -170,8 +173,14 @@ } private RootSetBuilder( - AppView<? extends AppInfoWithClassHierarchy> appView, SubtypingInfo subtypingInfo) { - this(appView, subtypingInfo, null); + AppView<? extends AppInfoWithClassHierarchy> appView, + Enqueuer enqueuer, + SubtypingInfo subtypingInfo) { + this( + appView, + RootSetBuilderEventConsumer.create(enqueuer.getArtProfileCollectionAdditions()), + subtypingInfo, + null); } boolean isMainDexRootSetBuilder() { @@ -1582,32 +1591,7 @@ ProgramMethod method = item.asMethod(); ProgramMethod companion = interfaceDesugaringSyntheticHelper.ensureMethodOfProgramCompanionClassStub( - method, - new InterfaceMethodDesugaringBaseEventConsumer() { - - @Override - public void acceptCompanionClassClinit(ProgramMethod method) { - // No processing of synthesized CC.<clinit>. They will be picked up by tracing. - } - - @Override - public void acceptDefaultAsCompanionMethod( - ProgramMethod method, ProgramMethod companionMethod) { - // The move will be included in the pending-inverse map below. - } - - @Override - public void acceptPrivateAsCompanionMethod( - ProgramMethod method, ProgramMethod companion) { - // The move will be included in the pending-inverse map below. - } - - @Override - public void acceptStaticAsCompanionMethod( - ProgramMethod method, ProgramMethod companion) { - // The move will be included in the pending-inverse map below. - } - }); + method, eventConsumer); // Add the method to the inverse map as tracing will now directly target the CC method. pendingMethodMoveInverse.put(companion, method); // Only shrinking and optimization are transferred for interface companion methods. @@ -2179,15 +2163,22 @@ } public static RootSetBuilder builder( - AppView<? extends AppInfoWithClassHierarchy> appView, SubtypingInfo subtypingInfo) { - return new RootSetBuilder(appView, subtypingInfo); + AppView<? extends AppInfoWithClassHierarchy> appView, + Enqueuer enqueuer, + SubtypingInfo subtypingInfo) { + return new RootSetBuilder(appView, enqueuer, subtypingInfo); } public static RootSetBuilder builder( AppView<? extends AppInfoWithClassHierarchy> appView, + ArtProfileCollectionAdditions artProfileCollectionAdditions, SubtypingInfo subtypingInfo, Iterable<? extends ProguardConfigurationRule> rules) { - return new RootSetBuilder(appView, subtypingInfo, rules); + return new RootSetBuilder( + appView, + RootSetBuilderEventConsumer.create(artProfileCollectionAdditions), + subtypingInfo, + rules); } } @@ -2197,9 +2188,13 @@ private ConsequentRootSetBuilder( AppView<? extends AppInfoWithClassHierarchy> appView, - SubtypingInfo subtypingInfo, - Enqueuer enqueuer) { - super(appView, subtypingInfo, null); + Enqueuer enqueuer, + SubtypingInfo subtypingInfo) { + super( + appView, + RootSetBuilderEventConsumer.create(enqueuer.getArtProfileCollectionAdditions()), + subtypingInfo, + null); this.enqueuer = enqueuer; } @@ -2235,9 +2230,9 @@ static ConsequentRootSetBuilder builder( AppView<? extends AppInfoWithClassHierarchy> appView, - SubtypingInfo subtypingInfo, - Enqueuer enqueuer) { - return new ConsequentRootSetBuilder(appView, subtypingInfo, enqueuer); + Enqueuer enqueuer, + SubtypingInfo subtypingInfo) { + return new ConsequentRootSetBuilder(appView, enqueuer, subtypingInfo); } } @@ -2245,9 +2240,14 @@ private MainDexRootSetBuilder( AppView<? extends AppInfoWithClassHierarchy> appView, + ArtProfileCollectionAdditions artProfileCollectionAdditions, SubtypingInfo subtypingInfo, Iterable<? extends ProguardConfigurationRule> rules) { - super(appView, subtypingInfo, rules); + super( + appView, + RootSetBuilderEventConsumer.create(artProfileCollectionAdditions), + subtypingInfo, + rules); } @Override @@ -2299,9 +2299,11 @@ public static MainDexRootSetBuilder builder( AppView<? extends AppInfoWithClassHierarchy> appView, + ArtProfileCollectionAdditions artProfileCollectionAdditions, SubtypingInfo subtypingInfo, Iterable<? extends ProguardConfigurationRule> rules) { - return new MainDexRootSetBuilder(appView, subtypingInfo, rules); + return new MainDexRootSetBuilder( + appView, artProfileCollectionAdditions, subtypingInfo, rules); } @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java index da2b320..dc4c7ea 100644 --- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java +++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -537,9 +537,9 @@ // somewhat expensive. if (appView.options().apiModelingOptions().isApiCallerIdentificationEnabled()) { ComputedApiLevel sourceApiLevel = - getApiReferenceLevelForMerging(appView, apiLevelCompute, sourceClass); + getApiReferenceLevelForMerging(apiLevelCompute, sourceClass); ComputedApiLevel targetApiLevel = - getApiReferenceLevelForMerging(appView, apiLevelCompute, targetClass); + getApiReferenceLevelForMerging(apiLevelCompute, targetClass); if (!sourceApiLevel.equals(targetApiLevel)) { if (Log.ENABLED) { AbortReason.API_REFERENCE_LEVEL.printLogMessageForClass(sourceClass);
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java index ebb3ba0..8cf1e77 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -197,15 +197,7 @@ appView.setAppInfo(appView.appInfo().rebuildWithClassHierarchy(result.commit)); appView.setAppInfo(appView.appInfo().rebuildWithMainDexInfo(result.mainDexInfo)); if (result.lens != null) { - appView.setGraphLens(result.lens); - appView.setAppInfo( - appView - .appInfo() - .rebuildWithMainDexInfo( - appView - .appInfo() - .getMainDexInfo() - .rewrittenWithLens(appView.getSyntheticItems(), result.lens))); + appView.rewriteWithLens(result.lens); } appView.pruneItems(result.prunedItems, executorService); } @@ -572,7 +564,7 @@ DexProgramClass externalSyntheticClass, AppView<?> appView) { if (shouldAnnotateSynthetics(appView.options())) { - SyntheticMarker.addMarkerToClass(externalSyntheticClass, kind, appView.options()); + SyntheticMarker.addMarkerToClass(externalSyntheticClass, kind, appView); } }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java index d8ae369..631231c 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -466,6 +466,16 @@ return pending.containsTypeOfKind(type, kind) || committed.containsTypeOfKind(type, kind); } + public Iterable<SyntheticKind> getSyntheticKinds(DexType type) { + Iterable<SyntheticKind> references = + IterableUtils.transform(committed.getItems(type), SyntheticReference::getKind); + SyntheticDefinition<?, ?, ?> definition = pending.definitions.get(type); + if (definition != null) { + references = Iterables.concat(references, IterableUtils.singleton(definition.getKind())); + } + return references; + } + boolean isSyntheticInput(DexProgramClass clazz) { return committed.containsSyntheticInput(clazz.getType()); }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java index 67dbe6a..aebc9ae 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java
@@ -6,14 +6,15 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.ClassAccessFlags; import com.android.tools.r8.graph.DexAnnotation; +import com.android.tools.r8.graph.DexAnnotation.SynthesizedAnnotationClassInfo; import com.android.tools.r8.graph.DexAnnotationSet; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind; +import com.android.tools.r8.utils.AndroidApiLevelUtils; import com.android.tools.r8.utils.DescriptorUtils; -import com.android.tools.r8.utils.InternalOptions; import java.nio.charset.StandardCharsets; import org.objectweb.asm.Attribute; import org.objectweb.asm.ByteVector; @@ -108,14 +109,18 @@ } public static void addMarkerToClass( - DexProgramClass clazz, SyntheticKind kind, InternalOptions options) { + DexProgramClass clazz, SyntheticKind kind, AppView<?> appView) { // TODO(b/158159959): Consider moving this to the dex writer similar to the CF case. - assert !options.isGeneratingClassFiles(); + assert !appView.options().isGeneratingClassFiles(); clazz.setAnnotations( clazz .annotations() .getWithAddedOrReplaced( - DexAnnotation.createAnnotationSynthesizedClass(kind, options.itemFactory))); + DexAnnotation.createAnnotationSynthesizedClass( + kind, + appView.options().itemFactory, + AndroidApiLevelUtils.getApiReferenceLevelForMerging( + appView.apiLevelCompute(), clazz)))); } public static SyntheticMarker stripMarkerFromClass(DexProgramClass clazz, AppView<?> appView) { @@ -134,7 +139,10 @@ SyntheticMarker marker = internalStripMarkerFromClass(clazz, appView); assert marker != NO_MARKER || !DexAnnotation.hasSynthesizedClassAnnotation( - clazz.annotations(), appView.dexItemFactory(), appView.getSyntheticItems()); + clazz.annotations(), + appView.dexItemFactory(), + appView.getSyntheticItems(), + appView.apiLevelCompute()); return marker; } @@ -146,13 +154,17 @@ if (isDefinitelyNotSyntheticProgramClass(clazz)) { return NO_MARKER; } - SyntheticKind kind = + SynthesizedAnnotationClassInfo synthesizedInfo = DexAnnotation.getSynthesizedClassAnnotationInfo( - clazz.annotations(), appView.dexItemFactory(), appView.getSyntheticItems()); - if (kind == null) { + clazz.annotations(), + appView.dexItemFactory(), + appView.getSyntheticItems(), + appView.apiLevelCompute()); + if (synthesizedInfo == null) { return NO_MARKER; } assert clazz.annotations().size() == 1; + SyntheticKind kind = synthesizedInfo.getSyntheticKind(); if (kind.isSingleSyntheticMethod()) { if (!clazz.interfaces.isEmpty()) { return NO_MARKER; @@ -164,6 +176,7 @@ } } clazz.setAnnotations(DexAnnotationSet.empty()); + clazz.forEachMethod(method -> method.setApiLevelForCode(synthesizedInfo.getComputedApiLevel())); DexType context = getSyntheticContextType(clazz.type, kind, appView.dexItemFactory()); return new SyntheticMarker( kind, SynthesizingContext.fromSyntheticInputClass(clazz, context, appView));
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java index 59aff72..5c81e4d 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -68,21 +68,25 @@ // Method synthetics. public final SyntheticKind ENUM_UNBOXING_CHECK_NOT_ZERO_METHOD = - generator.forSingleMethod("CheckNotZero"); - public final SyntheticKind RECORD_HELPER = generator.forSingleMethod("Record"); - public final SyntheticKind BACKPORT = generator.forSingleMethod("Backport"); + generator.forSingleMethodWithGlobalMerging("CheckNotZero"); + public final SyntheticKind RECORD_HELPER = generator.forSingleMethodWithGlobalMerging("Record"); + public final SyntheticKind BACKPORT = generator.forSingleMethodWithGlobalMerging("Backport"); public final SyntheticKind BACKPORT_WITH_FORWARDING = generator.forSingleMethod("BackportWithForwarding"); public final SyntheticKind STATIC_INTERFACE_CALL = generator.forSingleMethod("StaticInterfaceCall"); - public final SyntheticKind TO_STRING_IF_NOT_NULL = generator.forSingleMethod("ToStringIfNotNull"); - public final SyntheticKind THROW_CCE_IF_NOT_NULL = generator.forSingleMethod("ThrowCCEIfNotNull"); - public final SyntheticKind THROW_IAE = generator.forSingleMethod("ThrowIAE"); - public final SyntheticKind THROW_ICCE = generator.forSingleMethod("ThrowICCE"); - public final SyntheticKind THROW_NSME = generator.forSingleMethod("ThrowNSME"); - public final SyntheticKind THROW_RTE = generator.forSingleMethod("ThrowRTE"); - public final SyntheticKind TWR_CLOSE_RESOURCE = generator.forSingleMethod("TwrCloseResource"); - public final SyntheticKind SERVICE_LOADER = generator.forSingleMethod("ServiceLoad"); + public final SyntheticKind TO_STRING_IF_NOT_NULL = + generator.forSingleMethodWithGlobalMerging("ToStringIfNotNull"); + public final SyntheticKind THROW_CCE_IF_NOT_NULL = + generator.forSingleMethodWithGlobalMerging("ThrowCCEIfNotNull"); + public final SyntheticKind THROW_IAE = generator.forSingleMethodWithGlobalMerging("ThrowIAE"); + public final SyntheticKind THROW_ICCE = generator.forSingleMethodWithGlobalMerging("ThrowICCE"); + public final SyntheticKind THROW_NSME = generator.forSingleMethodWithGlobalMerging("ThrowNSME"); + public final SyntheticKind THROW_RTE = generator.forSingleMethodWithGlobalMerging("ThrowRTE"); + public final SyntheticKind TWR_CLOSE_RESOURCE = + generator.forSingleMethodWithGlobalMerging("TwrCloseResource"); + public final SyntheticKind SERVICE_LOADER = + generator.forSingleMethodWithGlobalMerging("ServiceLoad"); public final SyntheticKind OUTLINE = generator.forSingleMethod("Outline"); public final SyntheticKind COVARIANT_OUTLINE = generator.forSingleMethod("CovariantOutline"); public final SyntheticKind API_CONVERSION = generator.forSingleMethod("APIConversion"); @@ -90,7 +94,10 @@ generator.forSingleMethod("APIConversionParameters"); public final SyntheticKind COLLECTION_CONVERSION = generator.forSingleMethod("$CollectionConversion"); - public final SyntheticKind API_MODEL_OUTLINE = generator.forSingleMethod("ApiModelOutline"); + public final SyntheticKind API_MODEL_OUTLINE = + generator.forSingleMethodWithGlobalMerging("ApiModelOutline"); + public final SyntheticKind API_MODEL_OUTLINE_WITHOUT_GLOBAL_MERGING = + generator.forSingleMethod("ApiModelOutline"); private final List<SyntheticKind> ALL_KINDS; private String lazyVersionHash = null; @@ -144,7 +151,11 @@ } SyntheticKind forSingleMethod(String descriptor) { - return register(new SyntheticMethodKind(getNextId(), descriptor)); + return register(new SyntheticMethodKind(getNextId(), descriptor, false)); + } + + SyntheticKind forSingleMethodWithGlobalMerging(String descriptor) { + return register(new SyntheticMethodKind(getNextId(), descriptor, true)); } // TODO(b/214901256): Remove once fixed. @@ -217,6 +228,14 @@ return descriptor; } + public boolean isSyntheticMethodKind() { + return false; + } + + public SyntheticMethodKind asSyntheticMethodKind() { + return null; + } + public abstract boolean isShareable(); public abstract boolean isSingleSyntheticMethod(); @@ -236,10 +255,13 @@ public abstract void internalHash(Hasher hasher); } - private static class SyntheticMethodKind extends SyntheticKind { + public static class SyntheticMethodKind extends SyntheticKind { - public SyntheticMethodKind(int id, String descriptor) { + private final boolean allowGlobalMerging; + + public SyntheticMethodKind(int id, String descriptor, boolean allowGlobalMerging) { super(id, descriptor); + this.allowGlobalMerging = allowGlobalMerging; } @Override @@ -268,6 +290,20 @@ return false; } + public boolean isAllowGlobalMerging() { + return allowGlobalMerging; + } + + @Override + public boolean isSyntheticMethodKind() { + return true; + } + + @Override + public SyntheticMethodKind asSyntheticMethodKind() { + return this; + } + @Override public void internalHash(Hasher hasher) { hasher.putString("method", StandardCharsets.UTF_8);
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java index 5be322e..2e7e2e3 100644 --- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java +++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
@@ -99,6 +99,8 @@ case V39: return AndroidApiLevel.P; case V40: + return AndroidApiLevel.R; + case V41: return AndroidApiLevel.ANDROID_PLATFORM; default: throw new Unreachable();
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java index 841bf9a..3067de7 100644 --- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java +++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
@@ -65,20 +65,24 @@ } public static ComputedApiLevel getApiReferenceLevelForMerging( - AppView<?> appView, AndroidApiLevelCompute apiLevelCompute, DexProgramClass clazz) { + AndroidApiLevelCompute apiLevelCompute, DexProgramClass clazz) { // The api level of a class is the max level of it's members, super class and interfaces. return getMembersApiReferenceLevelForMerging( clazz, apiLevelCompute.computeApiLevelForDefinition(clazz.allImmediateSupertypes())); } - private static ComputedApiLevel getMembersApiReferenceLevelForMerging( + public static ComputedApiLevel getMembersApiReferenceLevelForMerging( DexProgramClass clazz, ComputedApiLevel memberLevel) { // Based on b/138781768#comment57 there is almost no penalty for having an unknown reference // as long as we are not invoking or accessing a field on it. Therefore we can disregard static // types of fields and only consider method code api levels. for (DexEncodedMethod method : clazz.methods()) { if (method.hasCode()) { - memberLevel = memberLevel.max(method.getApiLevelForCode()); + ComputedApiLevel apiLevelForCode = method.getApiLevelForCode(); + if (apiLevelForCode.isNotSetApiLevel()) { + return ComputedApiLevel.notSet(); + } + memberLevel = memberLevel.max(apiLevelForCode); } if (memberLevel.isUnknownApiLevel()) { return memberLevel;
diff --git a/src/main/java/com/android/tools/r8/utils/BitUtils.java b/src/main/java/com/android/tools/r8/utils/BitUtils.java index b264b4b..f951031 100644 --- a/src/main/java/com/android/tools/r8/utils/BitUtils.java +++ b/src/main/java/com/android/tools/r8/utils/BitUtils.java
@@ -13,4 +13,9 @@ public static boolean isBitInMaskSet(int value, int mask) { return (value & mask) != 0; } + + public static boolean isAligned(int alignment, int value) { + assert (alignment & (alignment - 1)) == 0; // Check alignment is power of 2. + return (value & (alignment - 1)) == 0; + } }
diff --git a/src/main/java/com/android/tools/r8/utils/DexVersion.java b/src/main/java/com/android/tools/r8/utils/DexVersion.java index d094cf7..e1b4588 100644 --- a/src/main/java/com/android/tools/r8/utils/DexVersion.java +++ b/src/main/java/com/android/tools/r8/utils/DexVersion.java
@@ -13,7 +13,8 @@ V37(37, new byte[] {'0', '3', '7'}), V38(38, new byte[] {'0', '3', '8'}), V39(39, new byte[] {'0', '3', '9'}), - V40(40, new byte[] {'0', '4', '0'}); + V40(40, new byte[] {'0', '4', '0'}), + V41(41, new byte[] {'0', '4', '1'}); private final int dexVersion; @@ -47,6 +48,8 @@ case Sv2: case S: case R: + // Dex version should have been V40 starting from API level 30, see b/269089718. + // return DexVersion.V40; case Q: case P: return DexVersion.V39; @@ -97,6 +100,8 @@ return Optional.of(V39); case 40: return Optional.of(V40); + case 41: + return Optional.of(V41); default: return Optional.empty(); }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java index e7dfeee..6e39e8e 100644 --- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java +++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -154,7 +154,7 @@ public static final int SUPPORTED_DEX_VERSION = AndroidApiLevel.LATEST.getDexVersion().getIntValue(); - public static final int EXPERIMENTAL_DEX_VERSION = DexVersion.V40.getIntValue(); + public static final int EXPERIMENTAL_DEX_VERSION = DexVersion.V41.getIntValue(); public static final int ASM_VERSION = Opcodes.ASM9; @@ -839,7 +839,7 @@ private final ApiModelTestingOptions apiModelTestingOptions = new ApiModelTestingOptions(); private final DesugarSpecificOptions desugarSpecificOptions = new DesugarSpecificOptions(); private final MappingComposeOptions mappingComposeOptions = new MappingComposeOptions(); - private final ArtProfileOptions artProfileOptions = new ArtProfileOptions(); + private final ArtProfileOptions artProfileOptions = new ArtProfileOptions(this); private final StartupOptions startupOptions = new StartupOptions(); private final StartupInstrumentationOptions startupInstrumentationOptions = new StartupInstrumentationOptions(); @@ -1909,18 +1909,6 @@ public void disableStubbingOfClasses() { enableStubbingOfClasses = false; } - - private boolean isThrowable(AppView<?> appView, DexLibraryClass libraryClass) { - DexClass current = libraryClass; - while (current.getSuperType() != null) { - DexType superType = current.getSuperType(); - if (superType == appView.dexItemFactory().throwableType) { - return true; - } - current = appView.definitionFor(current.getSuperType()); - } - return false; - } } public static class ProtoShrinkingOptions { @@ -1972,12 +1960,14 @@ // If false, use the desugared library implementation when desugared library is enabled. public boolean alwaysBackportListSetMapMethods = true; public boolean neverReuseCfLocalRegisters = false; - public boolean roundtripThroughLIR = false; + public boolean roundtripThroughLir = false; public boolean checkReceiverAlwaysNullInCallSiteOptimization = true; public boolean forceInlineAPIConversions = false; private boolean hasReadCheckDeterminism = false; private DeterminismChecker determinismChecker = null; public boolean usePcEncodingInCfForTesting = false; + public boolean dexVersion40FromApiLevel30 = + System.getProperty("com.android.tools.r8.dexVersion40ForApiLevel30") != null; public boolean dexContainerExperiment = System.getProperty("com.android.tools.r8.dexContainerExperiment") != null;
diff --git a/src/main/java/com/android/tools/r8/utils/ProgramConsumerUtils.java b/src/main/java/com/android/tools/r8/utils/ProgramConsumerUtils.java new file mode 100644 index 0000000..e33f72e --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/ProgramConsumerUtils.java
@@ -0,0 +1,32 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.utils; + +import com.android.tools.r8.ClassFileConsumer; +import com.android.tools.r8.DexFilePerClassFileConsumer; +import com.android.tools.r8.DexIndexedConsumer; +import com.android.tools.r8.ProgramConsumer; +import com.android.tools.r8.dex.Marker.Backend; + +public class ProgramConsumerUtils { + + public static Backend getBackend(ProgramConsumer programConsumer) { + if (isGeneratingClassFiles(programConsumer)) { + return Backend.CF; + } else { + assert isGeneratingDex(programConsumer); + return Backend.DEX; + } + } + + public static boolean isGeneratingClassFiles(ProgramConsumer programConsumer) { + return programConsumer instanceof ClassFileConsumer; + } + + public static boolean isGeneratingDex(ProgramConsumer programConsumer) { + return programConsumer instanceof DexIndexedConsumer + || programConsumer instanceof DexFilePerClassFileConsumer; + } +}
diff --git a/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java b/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java index 4fda093..88e998c 100644 --- a/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java +++ b/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java
@@ -8,6 +8,7 @@ import com.android.tools.r8.DataResourceProvider.Visitor; import com.android.tools.r8.ProgramResource.Kind; +import com.android.tools.r8.dex.Marker; import com.android.tools.r8.dex.Marker.Tool; import com.android.tools.r8.dump.DumpOptions; import com.android.tools.r8.origin.Origin; @@ -42,7 +43,7 @@ @Test public void test() throws Exception { InternalOptions options = new InternalOptions(); - options.dumpOptions = DumpOptions.builder(Tool.D8).build(); + options.dumpOptions = DumpOptions.builder(Tool.D8).setBackend(Marker.Backend.DEX).build(); String dataResourceName = "my-resource.bin"; byte[] dataResourceData = new byte[] {1, 2, 3};
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java index 74620e9..d4123d3 100644 --- a/src/test/java/com/android/tools/r8/TestBase.java +++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -46,6 +46,7 @@ import com.android.tools.r8.jasmin.JasminBuilder; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.origin.PathOrigin; +import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions; import com.android.tools.r8.references.ClassReference; import com.android.tools.r8.references.FieldReference; import com.android.tools.r8.references.MethodReference; @@ -856,14 +857,20 @@ computeAppViewWithClassHierarchy(app, keepConfig, optionsConsumer); // Run the tree shaker to compute an instance of AppInfoWithLiveness. ExecutorService executor = Executors.newSingleThreadExecutor(); + ArtProfileCollectionAdditions artProfileCollectionAdditions = + ArtProfileCollectionAdditions.nop(); SubtypingInfo subtypingInfo = SubtypingInfo.create(appView); RootSet rootSet = RootSet.builder( - appView, subtypingInfo, appView.options().getProguardConfiguration().getRules()) + appView, + artProfileCollectionAdditions, + subtypingInfo, + appView.options().getProguardConfiguration().getRules()) .build(executor); appView.setRootSet(rootSet); EnqueuerResult enqueuerResult = - EnqueuerFactory.createForInitialTreeShaking(appView, executor, subtypingInfo) + EnqueuerFactory.createForInitialTreeShaking( + appView, artProfileCollectionAdditions, executor, subtypingInfo) .traceApplication(rootSet, executor, Timing.empty()); executor.shutdown(); // We do not run the tree pruner to ensure that the hierarchy is as designed and not modified
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java index 68b6fc0..9b44892 100644 --- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java +++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -57,6 +57,7 @@ options.testing.allowUnnecessaryDontWarnWildcards = false; options.horizontalClassMergerOptions().enable(); options.horizontalClassMergerOptions().setEnableInterfaceMerging(); + options.getArtProfileOptions().setEnableCompletenessCheckForTesting(true); options .getCfCodeAnalysisOptions() .setAllowUnreachableCfBlocks(false) @@ -481,6 +482,11 @@ return super.addLibraryProvider(provider); } + public T setUseDefaultRuntimeLibrary(boolean useDefaultRuntimeLibrary) { + this.useDefaultRuntimeLibrary = useDefaultRuntimeLibrary; + return self(); + } + @Override public T allowStdoutMessages() { allowStdoutMessages = true;
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java index 9f80832..8978240 100644 --- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java +++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
@@ -54,6 +54,7 @@ Set<String> removedTypeNames = new HashSet<>(); if (maxApiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.U)) { removedTypeNames.add("com.android.internal.util.Predicate"); + removedTypeNames.add("android.adservices.AdServicesVersion"); } return removedTypeNames; } @@ -72,10 +73,12 @@ ClassSubject clazz = inspector.clazz(type); if (!clazz.isPresent()) { if (!clazz.getOriginalName().startsWith("android.test") - && !clazz.getOriginalName().startsWith("junit") - && node.getAttributes().getNamedItem("module") == null) { + && !clazz.getOriginalName().startsWith("junit")) { assert exemptionList.contains(type) || hasRemoved(node); assert exemptionList.contains(type) || getRemoved(node).isLessThanOrEqualTo(maxApiLevel); + if (!hasRemoved(node)) { + exemptionList.remove(type); + } } continue; } @@ -110,6 +113,7 @@ } } } + assert exemptionList.isEmpty(); } private boolean isMethod(Node node) {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelD8GradleSetupTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelD8GradleSetupTest.java new file mode 100644 index 0000000..f0b5d28 --- /dev/null +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelD8GradleSetupTest.java
@@ -0,0 +1,286 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.apimodel; + +import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass; +import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod; +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.D8TestCompileResult; +import com.android.tools.r8.SingleTestRunResult; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestCompilerBuilder; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ThrowableConsumer; +import com.android.tools.r8.synthesis.globals.GlobalSyntheticsTestingConsumer; +import com.android.tools.r8.testing.AndroidBuildVersion; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.BooleanUtils; +import com.android.tools.r8.utils.ThrowingConsumer; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector; +import java.util.ArrayList; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +/*** + * This is a replication of b/268596049. + */ +@RunWith(Parameterized.class) +public class ApiModelD8GradleSetupTest extends TestBase { + + private static final AndroidApiLevel mockApiLevelOne = AndroidApiLevel.M; + private static final AndroidApiLevel mockApiLevelTwo = AndroidApiLevel.O; + private static final AndroidApiLevel mockApiLevelThree = AndroidApiLevel.R; + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception { + testBuilder + .addLibraryClasses(LibraryClassOne.class, LibraryClassTwo.class, LibraryClassThree.class) + .addDefaultRuntimeLibrary(parameters) + .setMinApi(parameters.getApiLevel()) + .apply(setMockApiLevelForClass(LibraryClassOne.class, mockApiLevelOne)) + .apply( + setMockApiLevelForMethod( + LibraryClassOne.class.getDeclaredMethod("foo"), mockApiLevelOne)) + .apply(setMockApiLevelForClass(LibraryClassTwo.class, mockApiLevelTwo)) + .apply( + setMockApiLevelForMethod( + LibraryClassTwo.class.getDeclaredMethod("bar"), mockApiLevelTwo)) + .apply(setMockApiLevelForClass(LibraryClassThree.class, mockApiLevelThree)) + .apply( + setMockApiLevelForMethod( + LibraryClassThree.class.getDeclaredMethod("baz"), mockApiLevelThree)) + .apply(ApiModelingTestHelper::enableApiCallerIdentification) + .apply(ApiModelingTestHelper::enableOutliningOfMethods) + .apply(ApiModelingTestHelper::enableStubbingOfClasses); + } + + private boolean willHorizontallyMergeOutlines() { + // After api level mockApiLevelTwo we only have a single outline and therefore will not merge. + return parameters.getApiLevel().isLessThan(mockApiLevelTwo); + } + + private boolean willStubLibraryClassThree() { + return parameters.getApiLevel().isLessThan(mockApiLevelThree); + } + + public AndroidApiLevel getApiLevelForRuntime() { + return parameters.isCfRuntime() + ? AndroidApiLevel.B + : parameters.getRuntime().asDex().maxSupportedApiLevel(); + } + + public boolean addToBootClasspath(Class<?> clazz) { + if (clazz == LibraryClassOne.class) { + return getApiLevelForRuntime().isGreaterThanOrEqualTo(mockApiLevelOne); + } + if (clazz == LibraryClassTwo.class) { + return getApiLevelForRuntime().isGreaterThanOrEqualTo(mockApiLevelTwo); + } + assert clazz == LibraryClassThree.class; + return getApiLevelForRuntime().isGreaterThanOrEqualTo(mockApiLevelThree); + } + + @Test + public void testReference() throws Exception { + assumeTrue(parameters.isCfRuntime()); + testForJvm() + .addProgramClasses(Main.class, ProgramClassOne.class, ProgramClassTwo.class) + .addAndroidBuildVersion(AndroidApiLevel.B) + .addLibraryClasses(LibraryClassOne.class, LibraryClassTwo.class, LibraryClassThree.class) + .run(parameters.getRuntime(), Main.class) + .apply(this::checkOutput); + } + + @Test + public void testD8DebugWithMerge() throws Exception { + assumeTrue(parameters.isDexRuntime()); + testD8( + CompilationMode.DEBUG, + this::inspectNumberOfClassesFromOutput, + HorizontallyMergedClassesInspector::assertNoClassesMerged); + } + + @Test + public void testD8ReleaseForApiLevelWithOutlining() throws Exception { + assumeTrue(parameters.isDexRuntime()); + assumeTrue(willHorizontallyMergeOutlines()); + testD8( + CompilationMode.RELEASE, + this::inspectNumberOfClassesFromOutput, + HorizontallyMergedClassesInspector::assertNoClassesMerged); + } + + @Test + public void testD8ReleaseForApiLevelWithNoOutlining() throws Exception { + assumeTrue(parameters.isDexRuntime()); + assumeFalse(willHorizontallyMergeOutlines()); + testD8( + CompilationMode.RELEASE, + this::inspectNumberOfClassesFromOutput, + HorizontallyMergedClassesInspector::assertNoClassesMerged); + } + + private void testD8( + CompilationMode mode, + ThrowingConsumer<CodeInspector, Exception> inspect, + ThrowableConsumer<HorizontallyMergedClassesInspector> horizontallyMergingConsumer) + throws Exception { + GlobalSyntheticsTestingConsumer globals = new GlobalSyntheticsTestingConsumer(); + D8TestCompileResult compileResultProgramClass = + compileIntermediate(mode, globals, ProgramClassOne.class); + D8TestCompileResult compileResultProgramClassTwo = + compileIntermediate(mode, globals, ProgramClassTwo.class); + D8TestCompileResult compileResultMain = + compileIntermediate( + mode, globals, Main.class, ProgramClassOne.class, ProgramClassTwo.class); + + if (willStubLibraryClassThree()) { + assertTrue(globals.isSingleGlobal()); + } else { + assertFalse(globals.hasGlobals()); + } + + List<Class<?>> bootClassPath = new ArrayList<>(); + if (addToBootClasspath(LibraryClassOne.class)) { + bootClassPath.add(LibraryClassOne.class); + } + if (addToBootClasspath(LibraryClassTwo.class)) { + bootClassPath.add(LibraryClassTwo.class); + } + if (addToBootClasspath(LibraryClassThree.class)) { + bootClassPath.add(LibraryClassThree.class); + } + + testForD8() + .setMode(mode) + .setUseDefaultRuntimeLibrary(false) + .apply(b -> b.getBuilder().addGlobalSyntheticsResourceProviders(globals.getProviders())) + .addProgramFiles( + compileResultProgramClass.writeToZip(), + compileResultProgramClassTwo.writeToZip(), + compileResultMain.writeToZip()) + .setMinApi(parameters.getApiLevel()) + .addAndroidBuildVersion(getApiLevelForRuntime()) + .addHorizontallyMergedClassesInspector(horizontallyMergingConsumer) + .compile() + .inspect(inspect) + .addBootClasspathFiles( + buildOnDexRuntime(parameters, bootClassPath.toArray(new Class<?>[0]))) + .run(parameters.getRuntime(), Main.class) + .apply(this::checkOutput); + } + + private D8TestCompileResult compileIntermediate( + CompilationMode mode, + GlobalSyntheticsTestingConsumer globals, + Class<?> programClass, + Class<?>... classpathClass) + throws Exception { + return testForD8(parameters.getBackend()) + .setMode(mode) + .setIntermediate(true) + .addProgramClasses(programClass) + .addClasspathClasses(classpathClass) + .apply(this::setupTestBuilder) + .apply(b -> b.getBuilder().setGlobalSyntheticsConsumer(globals)) + .compile(); + } + + private void checkOutput(SingleTestRunResult<?> runResult) { + runResult.assertSuccessWithOutputLines( + addToBootClasspath(LibraryClassOne.class) ? "LibraryClassOne::foo" : "Not calling foo()", + addToBootClasspath(LibraryClassTwo.class) ? "LibraryClassTwo::bar" : "Not calling bar()", + addToBootClasspath(LibraryClassThree.class) + ? "LibraryClassThree::baz" + : "Not calling baz()"); + } + + private void inspectNumberOfClassesFromOutput(CodeInspector inspector) { + // We always have Main, ProgramClassOne, ProgramClassTwo and AndroidBuildVersion as program + // classes. Depending on the api a number of synthetic classes. + int numberOfClasses = + 4 + + (willStubLibraryClassThree() ? 2 : 0) + + BooleanUtils.intValue(parameters.getApiLevel().isLessThan(mockApiLevelTwo)) + + BooleanUtils.intValue(parameters.getApiLevel().isLessThan(mockApiLevelOne)); + assertEquals(numberOfClasses, inspector.allClasses().size()); + assertThat(inspector.clazz(Main.class), isPresent()); + assertThat(inspector.clazz(ProgramClassOne.class), isPresent()); + } + + // Will be present from api level 23 + public static class LibraryClassOne { + + public static void foo() { + System.out.println("LibraryClassOne::foo"); + } + } + + // Will be present from api level 26 + public static class LibraryClassTwo { + + public static void bar() { + System.out.println("LibraryClassTwo::bar"); + } + } + + // Will be present form api level 30 + public static class LibraryClassThree { + + public void baz() { + System.out.println("LibraryClassThree::baz"); + } + } + + public static class ProgramClassOne { + + public static void callOneAndTwo() { + if (AndroidBuildVersion.VERSION >= 23) { + LibraryClassOne.foo(); + } else { + System.out.println("Not calling foo()"); + } + if (AndroidBuildVersion.VERSION >= 26) { + LibraryClassTwo.bar(); + } else { + System.out.println("Not calling bar()"); + } + } + } + + public static class ProgramClassTwo extends LibraryClassThree {} + + public static class Main { + + public static void main(String[] args) { + ProgramClassOne.callOneAndTwo(); + if (AndroidBuildVersion.VERSION >= 30) { + new ProgramClassTwo().baz(); + } else { + System.out.println("Not calling baz()"); + } + } + } +}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoUnknownMergeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoUnknownMergeTest.java new file mode 100644 index 0000000..67082dc --- /dev/null +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoUnknownMergeTest.java
@@ -0,0 +1,111 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.apimodel; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.references.ClassReference; +import com.android.tools.r8.references.Reference; +import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector; +import java.util.HashSet; +import java.util.Set; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class ApiModelNoUnknownMergeTest extends TestBase { + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + @Test + public void testR8() throws Exception { + Set<String> methodReferences = new HashSet<>(); + testForR8(parameters.getBackend()) + .addProgramClasses(Main.class, LibraryClassFooCaller.class, LibraryClassBarCaller.class) + .addLibraryClasses(LibraryClass.class) + .addDefaultRuntimeLibrary(parameters) + .setMinApi(parameters.getApiLevel()) + .addKeepMainRule(Main.class) + .enableInliningAnnotations() + .addOptionsModification( + options -> { + ClassReference fooCaller = Reference.classFromClass(LibraryClassFooCaller.class); + ClassReference barCaller = Reference.classFromClass(LibraryClassBarCaller.class); + options.apiModelingOptions().tracedMethodApiLevelCallback = + (methodReference, computedApiLevel) -> { + if ((methodReference.getHolderClass().equals(fooCaller) + && methodReference.getMethodName().equals("callFoo")) + || (methodReference.getHolderClass().equals(barCaller) + && methodReference.getMethodName().equals("callBar"))) { + methodReferences.add(methodReference.toSourceString()); + assertTrue(computedApiLevel.isUnknownApiLevel()); + } + }; + }) + .addHorizontallyMergedClassesInspector( + HorizontallyMergedClassesInspector::assertNoClassesMerged) + .compile() + .addBootClasspathClasses(LibraryClass.class) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines("LibraryClass::foo", "LibraryClass::bar"); + Set<String> expected = new HashSet<>(); + expected.add( + Reference.methodFromMethod(LibraryClassFooCaller.class.getDeclaredMethod("callFoo")) + .toSourceString()); + expected.add( + Reference.methodFromMethod(LibraryClassBarCaller.class.getDeclaredMethod("callBar")) + .toSourceString()); + // Ensure that the two caller methods has been visited. + assertEquals(expected, methodReferences); + } + + public static class LibraryClass { + + public static void foo() { + System.out.println("LibraryClass::foo"); + } + + public static void bar() { + System.out.println("LibraryClass::bar"); + } + } + + public static class LibraryClassFooCaller { + + @NeverInline + public static void callFoo() { + LibraryClass.foo(); + } + } + + public static class LibraryClassBarCaller { + + @NeverInline + public static void callBar() { + LibraryClass.bar(); + } + } + + public static class Main { + + public static void main(String[] args) { + LibraryClassFooCaller.callFoo(); + LibraryClassBarCaller.callBar(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedVirtualMethodStackTraceTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedVirtualMethodStackTraceTest.java index 12edd71..204afc7 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedVirtualMethodStackTraceTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedVirtualMethodStackTraceTest.java
@@ -7,19 +7,36 @@ import static com.android.tools.r8.naming.retrace.StackTrace.isSame; import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static com.android.tools.r8.utils.codeinspector.Matchers.notIf; import static org.hamcrest.MatcherAssert.assertThat; import com.android.tools.r8.NeverClassInline; -import com.android.tools.r8.NeverInline; +import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestRuntime.CfRuntime; import com.android.tools.r8.naming.retrace.StackTrace; +import com.android.tools.r8.utils.BooleanUtils; +import com.android.tools.r8.utils.InternalOptions.InlinerOptions; +import java.util.List; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; -public class MergedVirtualMethodStackTraceTest extends HorizontalClassMergingTestBase { - public MergedVirtualMethodStackTraceTest(TestParameters parameters) { - super(parameters); +@RunWith(Parameterized.class) +public class MergedVirtualMethodStackTraceTest extends TestBase { + + @Parameter(0) + public TestParameters parameters; + + @Parameter(1) + public boolean forceInlineOnly; + + @Parameterized.Parameters(name = "{0}, forceInlineOnly={1}") + public static List<Object[]> data() { + return buildParameters( + getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values()); } public StackTrace expectedStackTrace; @@ -30,7 +47,7 @@ expectedStackTrace = testForJvm() .addTestClasspath() - .run(CfRuntime.getSystemRuntime(), Program.Main.class) + .run(CfRuntime.getSystemRuntime(), Main.class) .assertFailure() .map(StackTrace::extractFromJvm); } @@ -38,63 +55,43 @@ @Test public void testR8() throws Exception { testForR8(parameters.getBackend()) - .addInnerClasses(Program.class) - .addKeepMainRule(Program.Main.class) + .addInnerClasses(getClass()) + .addKeepMainRule(Main.class) .addKeepAttributeLineNumberTable() .addKeepAttributeSourceFile() - .addDontWarn(C.class) - .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel()) + .applyIf( + forceInlineOnly, b -> b.addOptionsModification(InlinerOptions::setOnlyForceInlining)) .addHorizontallyMergedClassesInspector( - inspector -> inspector.assertMergedInto(Program.B.class, Program.A.class)) - .run(parameters.getRuntime(), Program.Main.class) + inspector -> inspector.assertMergedInto(B.class, A.class)) + .run(parameters.getRuntime(), Main.class) .inspectStackTrace( (stackTrace, codeInspector) -> { - assertThat(codeInspector.clazz(Program.A.class), isPresent()); - assertThat(codeInspector.clazz(Program.B.class), isAbsent()); + assertThat(codeInspector.clazz(A.class), notIf(isPresent(), !forceInlineOnly)); + assertThat(codeInspector.clazz(B.class), isAbsent()); assertThat(stackTrace, isSame(expectedStackTrace)); }); } - public static class C { - public static void foo() { - System.out.println("foo c"); + @NeverClassInline + public static class A { + public void foo() { + System.out.println("foo a"); } } - public static class Program { - @NeverClassInline - public static class A { - @NeverInline - public void foo() { - System.out.println("foo a"); - try { - // Undefined reference, prevents inlining. - C.foo(); - } catch (NoClassDefFoundError e) { - } - } + @NeverClassInline + public static class B { + public void foo() { + throw new RuntimeException(); } + } - @NeverClassInline - public static class B { - @NeverInline - public void foo() { - try { - // Undefined reference, prevents inlining. - C.foo(); - } catch (NoClassDefFoundError e) { - } - throw new RuntimeException(); - } - } - - public static class Main { - public static void main(String[] args) { - new A().foo(); - new B().foo(); - } + public static class Main { + public static void main(String[] args) { + new A().foo(); + new B().foo(); } } }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java index a29a6d5..28e63fa 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
@@ -29,6 +29,7 @@ import com.google.common.collect.ImmutableList; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Comparator; import java.util.List; @@ -219,4 +220,21 @@ assertEquals(6, html.stream().filter(s -> s.contains("Flow")).count()); } } + + public static void main(String[] args) throws Exception { + // Generate all html docs. + Path folder = Paths.get("html"); + Files.createDirectories(folder); + ImmutableList<LibraryDesugaringSpecification> specs = + ImmutableList.of(JDK8, JDK11_MINIMAL, JDK11, JDK11_PATH, JDK11_LEGACY); + for (LibraryDesugaringSpecification spec : specs) { + Path jdkLibJar = + spec == JDK8 + ? ToolHelper.DESUGARED_JDK_8_LIB_JAR + : LibraryDesugaringSpecification.getTempLibraryJDK11Undesugar(); + new GenerateHtmlDoc( + spec.getSpecification().toString(), jdkLibJar.toString(), folder.toString()) + .run(spec + ".html"); + } + } }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java index 7240267..6e3ea69 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.desugar.desugaredlibrary; +import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11; import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_MINIMAL; import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH; import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK8; @@ -12,9 +13,12 @@ import com.android.tools.r8.TestParameters; import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification; +import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.DexMember; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses; -import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedMethodsGenerator; +import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.MemberAnnotation; +import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClassesGenerator; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.InternalOptions; import com.google.common.collect.ImmutableList; @@ -116,36 +120,66 @@ + " java.time.format.DateTimeFormatterBuilder.appendGenericZoneText(java.time.format.TextStyle," + " java.util.Set)"); + private static final Set<String> FAILURES_JAPANESE_ERA = + ImmutableSet.of("Field java.time.chrono.JapaneseEra java.time.chrono.JapaneseEra.REIWA"); + @Test public void test() throws Exception { SupportedClasses supportedClasses = - new SupportedMethodsGenerator(new InternalOptions()) + new SupportedClassesGenerator(new InternalOptions()) .run(librarySpecification.getDesugarJdkLibs(), librarySpecification.getSpecification()); for (AndroidApiLevel api : getRelevantApiLevels()) { - Set<DexMethod> localFailures = Sets.newIdentityHashSet(); + + Set<DexMethod> localMethodFailures = Sets.newIdentityHashSet(); + Set<DexField> localFieldFailures = Sets.newIdentityHashSet(); + supportedClasses.forEachClass( - supportedClass -> - supportedClass.forEachMethodAndAnnotation( - (method, annotation) -> { - if (annotation != null && annotation.isUnsupportedInMinApiRange()) { - if (api.getLevel() >= annotation.getMinRange() - && api.getLevel() <= annotation.getMaxRange()) { - localFailures.add(method.getReference()); - } - } - })); - Set<String> expectedFailures = getExpectedFailures(api); - Set<String> apiFailuresString = - localFailures.stream().map(DexMethod::toString).collect(Collectors.toSet()); - if (!expectedFailures.equals(apiFailuresString)) { - System.out.println("Failure for api " + api); - assertEquals(expectedFailures, apiFailuresString); - } + supportedClass -> { + supportedClass.forEachMethodAndAnnotation( + (method, annotation) -> { + if (missingFromRange(api, annotation)) { + localMethodFailures.add(method.getReference()); + } + }); + supportedClass.forEachFieldAndAnnotation( + (field, annotation) -> { + if (missingFromRange(api, annotation)) { + localFieldFailures.add(field.getReference()); + } + }); + }); + + assertStringEqualsAtApi(localFieldFailures, getExpectedFieldFailures(api), api); + assertStringEqualsAtApi(localMethodFailures, getExpectedMethodFailures(api), api); } } - private Set<String> getExpectedFailures(AndroidApiLevel api) { + private void assertStringEqualsAtApi( + Set<? extends DexMember<?, ?>> found, Set<String> expected, AndroidApiLevel api) { + Set<String> apiFailuresString = + found.stream().map(DexMember::toString).collect(Collectors.toSet()); + assertEquals("Failure for api " + api, expected, apiFailuresString); + } + + private boolean missingFromRange(AndroidApiLevel api, MemberAnnotation annotation) { + if (annotation != null && annotation.isUnsupportedInMinApiRange()) { + return api.getLevel() >= annotation.getMinRange() + && api.getLevel() <= annotation.getMaxRange(); + } + return false; + } + + private Set<String> getExpectedFieldFailures(AndroidApiLevel api) { + if (librarySpecification == JDK11 || librarySpecification == JDK11_PATH) { + if (api.isGreaterThanOrEqualTo(AndroidApiLevel.O) && api.isLessThan(AndroidApiLevel.R)) { + return FAILURES_JAPANESE_ERA; + } + } + return ImmutableSet.of(); + } + + private Set<String> getExpectedMethodFailures(AndroidApiLevel api) { Set<String> expectedFailures = new HashSet<>(); boolean jdk11NonMinimal = librarySpecification != JDK8 && librarySpecification != JDK11_MINIMAL; if (jdk11NonMinimal && api.isGreaterThanOrEqualTo(AndroidApiLevel.N)) {
diff --git a/src/test/java/com/android/tools/r8/dex/container/DexContainerFormatBasicTest.java b/src/test/java/com/android/tools/r8/dex/container/DexContainerFormatBasicTest.java index d1f909d..41aa6d0 100644 --- a/src/test/java/com/android/tools/r8/dex/container/DexContainerFormatBasicTest.java +++ b/src/test/java/com/android/tools/r8/dex/container/DexContainerFormatBasicTest.java
@@ -6,12 +6,14 @@ import static com.android.tools.r8.dex.Constants.CHECKSUM_OFFSET; import static com.android.tools.r8.dex.Constants.DATA_OFF_OFFSET; import static com.android.tools.r8.dex.Constants.DATA_SIZE_OFFSET; +import static com.android.tools.r8.dex.Constants.DEX_MAGIC_SIZE; import static com.android.tools.r8.dex.Constants.FILE_SIZE_OFFSET; import static com.android.tools.r8.dex.Constants.MAP_OFF_OFFSET; import static com.android.tools.r8.dex.Constants.SIGNATURE_OFFSET; import static com.android.tools.r8.dex.Constants.STRING_IDS_OFF_OFFSET; import static com.android.tools.r8.dex.Constants.STRING_IDS_SIZE_OFFSET; import static com.android.tools.r8.dex.Constants.TYPE_STRING_ID_ITEM; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -26,7 +28,10 @@ import com.android.tools.r8.maindexlist.MainDexListTests; import com.android.tools.r8.transformers.ClassTransformer; import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.BitUtils; import com.android.tools.r8.utils.DescriptorUtils; +import com.android.tools.r8.utils.DexVersion; +import com.android.tools.r8.utils.ListUtils; import com.android.tools.r8.utils.ZipUtils; import com.google.common.collect.ImmutableList; import com.google.common.io.ByteStreams; @@ -80,7 +85,7 @@ .setMinApi(AndroidApiLevel.L) .compile() .writeToZip(); - validateDex(outputA, 2); + validateDex(outputA, 2, AndroidApiLevel.L.getDexVersion()); Path outputB = testForD8(Backend.DEX) @@ -88,7 +93,7 @@ .setMinApi(AndroidApiLevel.L) .compile() .writeToZip(); - validateDex(outputB, 2); + validateDex(outputB, 2, AndroidApiLevel.L.getDexVersion()); Path outputMerged = testForD8(Backend.DEX) @@ -96,7 +101,7 @@ .setMinApi(AndroidApiLevel.L) .compile() .writeToZip(); - validateDex(outputMerged, 4); + validateDex(outputMerged, 4, AndroidApiLevel.L.getDexVersion()); } @Test @@ -135,49 +140,79 @@ validateSingleContainerDex(outputB); } - private void validateDex(Path output, int expectedDexes) throws Exception { + private void validateDex(Path output, int expectedDexes, DexVersion expectedVersion) + throws Exception { List<byte[]> dexes = unzipContent(output); assertEquals(expectedDexes, dexes.size()); for (byte[] dex : dexes) { - validate(dex); + validate(dex, expectedVersion); } } private void validateSingleContainerDex(Path output) throws Exception { List<byte[]> dexes = unzipContent(output); assertEquals(1, dexes.size()); - validate(dexes.get(0)); + validate(dexes.get(0), DexVersion.V41); } - private void validate(byte[] dex) throws Exception { + private void validate(byte[] dex, DexVersion expectedVersion) throws Exception { CompatByteBuffer buffer = CompatByteBuffer.wrap(dex); setByteOrder(buffer); IntList sections = new IntArrayList(); int offset = 0; while (offset < buffer.capacity()) { + assertTrue(BitUtils.isAligned(4, offset)); sections.add(offset); int dataSize = buffer.getInt(offset + DATA_SIZE_OFFSET); int dataOffset = buffer.getInt(offset + DATA_OFF_OFFSET); + int file_size = buffer.getInt(offset + FILE_SIZE_OFFSET); offset = dataOffset + dataSize; + assertEquals(file_size, offset - ListUtils.last(sections)); } assertEquals(buffer.capacity(), offset); - int lastOffset = sections.getInt(sections.size() - 1); - int stringIdsSize = buffer.getInt(lastOffset + STRING_IDS_SIZE_OFFSET); - int stringIdsOffset = buffer.getInt(lastOffset + STRING_IDS_OFF_OFFSET); - for (Integer sectionOffset : sections) { - assertEquals(stringIdsSize, buffer.getInt(sectionOffset + STRING_IDS_SIZE_OFFSET)); - assertEquals(stringIdsOffset, buffer.getInt(sectionOffset + STRING_IDS_OFF_OFFSET)); - assertEquals(stringIdsSize, getSizeFromMap(TYPE_STRING_ID_ITEM, buffer, sectionOffset)); - assertEquals(stringIdsOffset, getOffsetFromMap(TYPE_STRING_ID_ITEM, buffer, sectionOffset)); + validateHeader(sections, buffer, sectionOffset, expectedVersion); validateMap(buffer, sectionOffset); validateSignature(buffer, sectionOffset); validateChecksum(buffer, sectionOffset); } } + private byte[] magicBytes(DexVersion version) { + byte[] magic = new byte[DEX_MAGIC_SIZE]; + System.arraycopy( + Constants.DEX_FILE_MAGIC_PREFIX, 0, magic, 0, Constants.DEX_FILE_MAGIC_PREFIX.length); + System.arraycopy( + version.getBytes(), + 0, + magic, + Constants.DEX_FILE_MAGIC_PREFIX.length, + version.getBytes().length); + magic[Constants.DEX_FILE_MAGIC_PREFIX.length + version.getBytes().length] = + Constants.DEX_FILE_MAGIC_SUFFIX; + assertEquals( + DEX_MAGIC_SIZE, Constants.DEX_FILE_MAGIC_PREFIX.length + version.getBytes().length + 1); + return magic; + } + + private void validateHeader( + IntList sections, CompatByteBuffer buffer, int offset, DexVersion expectedVersion) { + int lastOffset = sections.getInt(sections.size() - 1); + int stringIdsSize = buffer.getInt(lastOffset + STRING_IDS_SIZE_OFFSET); + int stringIdsOffset = buffer.getInt(lastOffset + STRING_IDS_OFF_OFFSET); + + byte[] magic = new byte[DEX_MAGIC_SIZE]; + buffer.get(magic); + assertArrayEquals(magicBytes(expectedVersion), magic); + + assertEquals(stringIdsSize, buffer.getInt(offset + STRING_IDS_SIZE_OFFSET)); + assertEquals(stringIdsOffset, buffer.getInt(offset + STRING_IDS_OFF_OFFSET)); + assertEquals(stringIdsSize, getSizeFromMap(TYPE_STRING_ID_ITEM, buffer, offset)); + assertEquals(stringIdsOffset, getOffsetFromMap(TYPE_STRING_ID_ITEM, buffer, offset)); + } + private void validateMap(CompatByteBuffer buffer, int offset) { int mapOffset = buffer.getInt(offset + MAP_OFF_OFFSET); buffer.position(mapOffset); @@ -199,9 +234,7 @@ int sectionSize = buffer.getInt(offset + FILE_SIZE_OFFSET); MessageDigest md = MessageDigest.getInstance("SHA-1"); md.update( - buffer.asByteBuffer().array(), - offset + FILE_SIZE_OFFSET, - sectionSize - offset - FILE_SIZE_OFFSET); + buffer.asByteBuffer().array(), offset + FILE_SIZE_OFFSET, sectionSize - FILE_SIZE_OFFSET); byte[] expectedSignature = new byte[20]; md.digest(expectedSignature, 0, 20); for (int i = 0; i < expectedSignature.length; i++) { @@ -213,9 +246,7 @@ int sectionSize = buffer.getInt(offset + FILE_SIZE_OFFSET); Adler32 adler = new Adler32(); adler.update( - buffer.asByteBuffer().array(), - offset + SIGNATURE_OFFSET, - sectionSize - offset - SIGNATURE_OFFSET); + buffer.asByteBuffer().array(), offset + SIGNATURE_OFFSET, sectionSize - SIGNATURE_OFFSET); assertEquals((int) adler.getValue(), buffer.getInt(offset + CHECKSUM_OFFSET)); }
diff --git a/src/test/java/com/android/tools/r8/dex/whitespaceinidentifiers/WhiteSpaceInIdentifiersTest.java b/src/test/java/com/android/tools/r8/dex/whitespaceinidentifiers/WhiteSpaceInIdentifiersTest.java new file mode 100644 index 0000000..1fad1ab --- /dev/null +++ b/src/test/java/com/android/tools/r8/dex/whitespaceinidentifiers/WhiteSpaceInIdentifiersTest.java
@@ -0,0 +1,357 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.dex.whitespaceinidentifiers; + +import static com.android.tools.r8.DiagnosticsMatcher.diagnosticException; +import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage; +import static org.hamcrest.CoreMatchers.allOf; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.ProguardVersion; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestCompilerBuilder; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.naming.ProguardMapReader.ParseException; +import com.android.tools.r8.naming.retrace.StackTrace; +import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine; +import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.DexVersion; +import com.android.tools.r8.utils.StringUtils; +import java.nio.file.Path; +import java.util.List; +import java.util.concurrent.ExecutionException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class WhiteSpaceInIdentifiersTest extends TestBase { + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static List<Object[]> data() { + return buildParameters(getTestParameters().withAllRuntimes().withAllApiLevels().build()); + } + + private static final String EXPECTED_OUTPUT = + StringUtils.lines( + "0x20", "0xa0", "0x1680", "0x2000", "0x2001", "0x2002", "0x2003", "0x2004", "0x2005", + "0x2006", "0x2007", "0x2008", "0x2009", "0x200a", "0x202f", "0x205f", "0x3000"); + + private void assumeParametersWithSupportForWhitespaceInIdentifiers() { + assumeTrue( + parameters.isCfRuntime() + || (parameters.isDexRuntime() + && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.R))); + } + + private void assumeDexRuntimeSupportingDexVersion039() { + assumeTrue( + parameters.isDexRuntime() + && parameters.getApiLevel().getDexVersion().isGreaterThanOrEqualTo(DexVersion.V39)); + } + + public void configure(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception { + testBuilder + .addProgramClassFileData(getTransformed()) + .applyIf(parameters.isDexRuntime(), b -> b.setMinApi(parameters.getApiLevel())) + .applyIf( + parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.R), + b -> { + try { + b.compileWithExpectedDiagnostics( + diagnostics -> + diagnostics.assertErrorsMatch( + diagnosticMessage( + containsString("are not allowed prior to DEX version 040")))); + fail("Unexpected success"); + } catch (CompilationFailedException e) { + // Expected. + } + }, + b -> + b.run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED_OUTPUT)); + } + + @Test + public void testD8() throws Exception { + parameters.assumeDexRuntime(); + configure(testForD8(parameters.getBackend())); + } + + @Test + public void testR8() throws Exception { + assumeParametersWithSupportForWhitespaceInIdentifiers(); + Exception e = + assertThrows( + RuntimeException.class, + () -> configure(testForR8(parameters.getBackend()).addKeepMainRule(TestClass.class))); + // TODO(b/141287396): Proguard maps with spaces in identifiers are not supported. Running + // on R8 always creates an inspector to find the name of the potentially renamed main class. + assertTrue(e.getCause() instanceof ExecutionException); + assertTrue(e.getCause().getCause() instanceof ParseException); + } + + @Test + public void testR8Mapping() throws Exception { + assumeParametersWithSupportForWhitespaceInIdentifiers(); + String map = + testForR8(parameters.getBackend()) + .addProgramClassFileData(getTransformed()) + .applyIf(parameters.isDexRuntime(), b -> b.setMinApi(parameters.getApiLevel())) + .addKeepMainRule(TestClass.class) + .compile() + .getProguardMap(); + // R8 renames methods with white space, so they appear in the mapping file. + assertTrue(StringUtils.splitLines(map).size() > 40); + } + + @Test + public void testProguard() throws Exception { + parameters.assumeCfRuntime(); + configure( + testForProguard(ProguardVersion.V7_0_0) + .addKeepMainRule(TestClass.class) + .addDontWarn(TestClass.class.getTypeName())); + } + + @Test + public void testProguardMapping() throws Exception { + parameters.assumeCfRuntime(); + String map = + testForProguard(ProguardVersion.V7_0_0) + .addProgramClassFileData(getTransformed()) + .addKeepMainRule(TestClass.class) + .addDontWarn(TestClass.class.getTypeName()) + .compile() + .getProguardMap(); + // Proguard leaves the methods with white space alone, so they don't need to appear in the + // mapping file. + assertEquals( + StringUtils.lines( + TestClass.class.getTypeName() + " -> " + TestClass.class.getTypeName() + ":", + " void <init>() -> <init>", + " void main(java.lang.String[]) -> main"), + map); + } + + @Test + public void testD8MergeWithSpaces() throws Exception { + assumeDexRuntimeSupportingDexVersion039(); + // Compile with API level 30 to allow white space in identifiers. This generates DEX + // version 039. + Path dex = + testForD8(parameters.getBackend()) + .addProgramClassFileData(getTransformed()) + .setMinApi(AndroidApiLevel.R) + .compile() + .writeToZip(); + + // Run merge step with DEX with white space in input (not forcing min API level of R). + testForD8(parameters.getBackend()) + .addProgramFiles(dex) + .setMinApi(parameters.getApiLevel()) + .applyIf( + parameters.getApiLevel().isLessThan(AndroidApiLevel.R), + b -> { + try { + // TODO(b/269089718): This should not be an AssertionError but a compilation error. + b.compileWithExpectedDiagnostics( + diagnostics -> + diagnostics.assertErrorsMatch(diagnosticException(AssertionError.class))); + fail("Unexpected success"); + } catch (CompilationFailedException e) { + // Expected. + } + }, + b -> + b.run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED_OUTPUT)); + } + + @Test + public void testD8RunWithSpaces() throws Exception { + assumeDexRuntimeSupportingDexVersion039(); + testForD8(parameters.getBackend()) + .addProgramClassFileData(getTransformed()) + .setMinApi(AndroidApiLevel.R) + .run(parameters.getRuntime(), TestClass.class) + .applyIf( + parameters.getApiLevel().isLessThan(AndroidApiLevel.R), + b -> b.assertFailureWithErrorThatMatches(containsString("Failure to verify dex file")), + b -> b.assertSuccessWithOutput(EXPECTED_OUTPUT)); + } + + @Test + public void testD8RunWithSpacesUsingDexV40() throws Exception { + assumeDexRuntimeSupportingDexVersion039(); + testForD8(parameters.getBackend()) + .addOptionsModification(options -> options.testing.dexVersion40FromApiLevel30 = true) + .addProgramClassFileData(getTransformed()) + .setMinApi(AndroidApiLevel.R) + .run(parameters.getRuntime(), TestClass.class) + .applyIf( + parameters.getApiLevel().isLessThan(AndroidApiLevel.R), + b -> + b.assertFailureWithErrorThatMatches( + allOf(containsString("Unrecognized version"), containsString("0 4 0"))), + b -> b.assertSuccessWithOutput(EXPECTED_OUTPUT)); + } + + @Test + public void testJvmStackTrace() throws Exception { + parameters.assumeCfRuntime(); + testForJvm() + .addProgramClassFileData(getTransformed()) + .run(parameters.asCfRuntime(), TestClass.class, "some-argument") + .assertFailureWithErrorThatThrows(RuntimeException.class) + .inspectOriginalStackTrace( + stackTrace -> + assertThat( + stackTrace, + StackTrace.isSameExceptForLineNumbers( + StackTrace.builder() + .add( + StackTraceLine.builder() + .setMethodName(" ") + .setClassName(TestClass.class.getTypeName()) + .setFileName("WhiteSpaceInIdentifiersTest.java") + .build()) + .add( + StackTraceLine.builder() + .setMethodName("main") + .setClassName(TestClass.class.getTypeName()) + .setFileName("WhiteSpaceInIdentifiersTest.java") + .build()) + .build()))); + } + + private String rename(String name) { + return new String(Character.toChars(Integer.parseInt(name.substring(1), 16))); + } + + private byte[] getTransformed() throws Exception { + return transformer(TestClass.class) + .renameMethod(MethodPredicate.onName(name -> name.startsWith("t")), this::rename) + .transformMethodInsnInMethod( + "main", + ((opcode, owner, name, descriptor, isInterface, continuation) -> { + continuation.visitMethodInsn( + opcode, + owner, + name.startsWith("t") ? rename(name) : name, + descriptor, + isInterface); + })) + .transform(); + } + + // Test with white space characters added in https://r8-review.git.corp.google.com/c/r8/+/42269. + // Supported on Art in https://android-review.git.corp.google.com/c/platform/art/+/1106719. + static class TestClass { + + private static void t20(boolean throwException) { + System.out.println("0x20"); + if (throwException) { + throw new RuntimeException(); + } + } + + private static void ta0() { + System.out.println("0xa0"); + } + + private static void t1680() { + System.out.println("0x1680"); + } + + private static void t2000() { + System.out.println("0x2000"); + } + + private static void t2001() { + System.out.println("0x2001"); + } + + private static void t2002() { + System.out.println("0x2002"); + } + + private static void t2003() { + System.out.println("0x2003"); + } + + private static void t2004() { + System.out.println("0x2004"); + } + + private static void t2005() { + System.out.println("0x2005"); + } + + private static void t2006() { + System.out.println("0x2006"); + } + + private static void t2007() { + System.out.println("0x2007"); + } + + private static void t2008() { + System.out.println("0x2008"); + } + + private static void t2009() { + System.out.println("0x2009"); + } + + private static void t200a() { + System.out.println("0x200a"); + } + + private static void t202f() { + System.out.println("0x202f"); + } + + private static void t205f() { + System.out.println("0x205f"); + } + + private static void t3000() { + System.out.println("0x3000"); + } + + public static void main(String[] args) { + t20(args.length > 0); + ta0(); + t1680(); + t2000(); + t2001(); + t2002(); + t2003(); + t2004(); + t2005(); + t2006(); + t2007(); + t2008(); + t2009(); + t200a(); + t202f(); + t205f(); + t3000(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java b/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java index c2d3a03..eef3dcf 100644 --- a/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java +++ b/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java
@@ -48,8 +48,6 @@ public void testR8() throws Exception { testForR8(Backend.DEX) .addProgramFiles(outDirectory.resolve("program.jar")) - .addOptionsModification( - options -> options.getArtProfileOptions().setEnableCompletenessCheckForTesting(true)) .apply(this::configure) .compile(); }
diff --git a/src/test/java/com/android/tools/r8/ir/InlineTest.java b/src/test/java/com/android/tools/r8/ir/InlineTest.java index be71b20..037bb82 100644 --- a/src/test/java/com/android/tools/r8/ir/InlineTest.java +++ b/src/test/java/com/android/tools/r8/ir/InlineTest.java
@@ -23,6 +23,7 @@ import com.android.tools.r8.ir.code.Instruction; import com.android.tools.r8.ir.code.InstructionListIterator; import com.android.tools.r8.origin.Origin; +import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions; import com.android.tools.r8.shaking.Enqueuer; import com.android.tools.r8.shaking.EnqueuerFactory; import com.android.tools.r8.shaking.EnqueuerResult; @@ -73,17 +74,21 @@ throws ExecutionException { AppView<AppInfoWithClassHierarchy> appView = AppView.createForR8(application.asDirect()); appView.setAppServices(AppServices.builder(appView).build()); + ArtProfileCollectionAdditions artProfileCollectionAdditions = + ArtProfileCollectionAdditions.nop(); ExecutorService executorService = ThreadUtils.getExecutorService(options); SubtypingInfo subtypingInfo = SubtypingInfo.create(appView); appView.setRootSet( RootSet.builder( appView, + artProfileCollectionAdditions, subtypingInfo, ImmutableList.of(ProguardKeepRule.defaultKeepAllRule(unused -> {}))) .build(executorService)); Timing timing = Timing.empty(); Enqueuer enqueuer = - EnqueuerFactory.createForInitialTreeShaking(appView, executorService, subtypingInfo); + EnqueuerFactory.createForInitialTreeShaking( + appView, artProfileCollectionAdditions, executorService, subtypingInfo); EnqueuerResult enqueuerResult = enqueuer.traceApplication(appView.rootSet(), executorService, timing); appView.setAppInfo(enqueuerResult.getAppInfo());
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java b/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java index b1899a3..8967763 100644 --- a/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java +++ b/src/test/java/com/android/tools/r8/ir/desugar/annotations/CovariantReturnTypeAnnotationTransformerTest.java
@@ -5,21 +5,30 @@ package com.android.tools.r8.ir.desugar.annotations; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; -import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import com.android.tools.r8.AsmTestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ToolHelper; -import com.android.tools.r8.utils.AndroidApp; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import com.google.common.collect.ImmutableList; +import java.nio.file.Path; import java.util.Collections; +import java.util.List; import org.junit.Assert; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; +@RunWith(Parameterized.class) public class CovariantReturnTypeAnnotationTransformerTest extends AsmTestBase { + public static final String PACKAGE_NAME = "com/android/tools/r8/ir/desugar/annotations"; public static final String CRT_BINARY_NAME = "dalvik/annotation/codegen/CovariantReturnType"; public static final String CRTS_INNER_NAME = "CovariantReturnTypes"; @@ -28,10 +37,18 @@ public static final String CRT_TYPE_NAME = CRT_BINARY_NAME.replace('/', '.'); public static final String CRTS_TYPE_NAME = CRT_BINARY_NAME.replace('/', '.'); + @Parameter(0) + public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withDexRuntimesAndAllApiLevels().build(); + } + @Test public void testVersion1WithClient1And2() throws Exception { - AndroidApp input = - buildAndroidApp( + List<byte[]> input = + ImmutableList.of( ToolHelper.getClassAsBytes(Client.class), ToolHelper.getClassAsBytes(A.class), ToolHelper.getClassAsBytes(B.class), @@ -46,8 +63,8 @@ @Test public void testVersion1WithClient3() throws Exception { - AndroidApp input = - buildAndroidApp( + List<byte[]> input = + ImmutableList.of( com.android.tools.r8.ir.desugar.annotations.version3.ClientDump.dump(), ToolHelper.getClassAsBytes(A.class), ToolHelper.getClassAsBytes(B.class), @@ -63,8 +80,8 @@ @Test public void testVersion2WithClient1And2() throws Exception { - AndroidApp input = - buildAndroidApp( + List<byte[]> input = + ImmutableList.of( ToolHelper.getClassAsBytes(Client.class), ToolHelper.getClassAsBytes(A.class), com.android.tools.r8.ir.desugar.annotations.version2.BDump.dump(), @@ -79,8 +96,8 @@ @Test public void testVersion2WithClient3() throws Exception { - AndroidApp input = - buildAndroidApp( + List<byte[]> input = + ImmutableList.of( com.android.tools.r8.ir.desugar.annotations.version3.ClientDump.dump(), ToolHelper.getClassAsBytes(A.class), com.android.tools.r8.ir.desugar.annotations.version2.BDump.dump(), @@ -100,8 +117,8 @@ @Test public void testVersion3WithClient3() throws Exception { - AndroidApp input = - buildAndroidApp( + List<byte[]> input = + ImmutableList.of( com.android.tools.r8.ir.desugar.annotations.version3.ClientDump.dump(), ToolHelper.getClassAsBytes(A.class), com.android.tools.r8.ir.desugar.annotations.version3.BDump.dump(), @@ -116,8 +133,8 @@ @Test public void testVersion3WithClient1And2() throws Exception { - AndroidApp input = - buildAndroidApp( + List<byte[]> input = + ImmutableList.of( ToolHelper.getClassAsBytes(Client.class), ToolHelper.getClassAsBytes(A.class), com.android.tools.r8.ir.desugar.annotations.version3.BDump.dump(), @@ -132,8 +149,8 @@ @Test public void testRepeatedCompilation() throws Exception { - AndroidApp input = - buildAndroidApp( + List<byte[]> input = + ImmutableList.of( ToolHelper.getClassAsBytes(Client.class), ToolHelper.getClassAsBytes(A.class), com.android.tools.r8.ir.desugar.annotations.version2.BDump.dump(), @@ -142,51 +159,73 @@ // Version 2 contains annotations. checkPresenceOfCovariantAnnotations(input, true); - AndroidApp output = - compileWithD8(input, options -> options.processCovariantReturnTypeAnnotations = true); - - // Compilation output does not contain annotations. - checkPresenceOfCovariantAnnotations(output, false); + Path output = + testForD8(parameters.getBackend()) + .addProgramClassFileData(input) + .addOptionsModification(options -> options.processCovariantReturnTypeAnnotations = true) + .setMinApi(parameters.getApiLevel()) + .compile() + // Compilation output does not contain annotations. + .inspect(inspector -> checkPresenceOfCovariantAnnotations(inspector, false)) + .writeToZip(); // Compilation will fail with a compilation error the second time if the implementation does // not remove the CovariantReturnType annotations properly during the first compilation. - compileWithD8(output, options -> options.processCovariantReturnTypeAnnotations = true); + testForD8(parameters.getBackend()) + .addProgramFiles(output) + .addOptionsModification(options -> options.processCovariantReturnTypeAnnotations = true) + .setMinApi(parameters.getApiLevel()) + .compile(); } private void succeedsWithOption( - AndroidApp input, boolean option, boolean checkPresenceOfSyntheticMethods) throws Exception { - AndroidApp output = - compileWithD8(input, options -> options.processCovariantReturnTypeAnnotations = option); - String stdout = runOnArt(output, Client.class.getCanonicalName()); - assertEquals(getExpectedOutput(), stdout); - checkPresenceOfCovariantAnnotations(output, false); - if (option && checkPresenceOfSyntheticMethods) { - checkPresenceOfSyntheticMethods(output); - } - } - - private void failsWithOption(AndroidApp input, boolean option) throws Exception { - AndroidApp output = - compileWithD8(input, options -> options.processCovariantReturnTypeAnnotations = option); - checkPresenceOfCovariantAnnotations(output, false); - ToolHelper.ProcessResult result = runOnArtRaw(output, Client.class.getCanonicalName()); - assertThat(result.stderr, containsString("java.lang.NoSuchMethodError")); - } - - private void succeedsIndependentOfFlag(AndroidApp input, boolean checkPresenceOfSyntheticMethods) + List<byte[]> input, boolean option, boolean checkPresenceOfSyntheticMethods) throws Exception { + testForD8(parameters.getBackend()) + .addProgramClassFileData(input) + .addOptionsModification(options -> options.processCovariantReturnTypeAnnotations = option) + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect( + inspector -> { + checkPresenceOfCovariantAnnotations(inspector, false); + if (option && checkPresenceOfSyntheticMethods) { + checkPresenceOfSyntheticMethods(inspector); + } + }) + .run(parameters.getRuntime(), Client.class.getCanonicalName()) + .assertSuccessWithOutput(getExpectedOutput()); + } + + private void failsWithOption(List<byte[]> input, boolean option) throws Exception { + testForD8(parameters.getBackend()) + .addProgramClassFileData(input) + .addOptionsModification(options -> options.processCovariantReturnTypeAnnotations = option) + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(inspector -> checkPresenceOfCovariantAnnotations(inspector, false)) + .run(parameters.getRuntime(), Client.class.getCanonicalName()) + .assertFailureWithErrorThatThrows(NoSuchMethodError.class); + } + + private void succeedsIndependentOfFlag( + List<byte[]> input, boolean checkPresenceOfSyntheticMethods) throws Exception { succeedsWithOption(input, true, checkPresenceOfSyntheticMethods); succeedsWithOption(input, false, checkPresenceOfSyntheticMethods); } - private void failsIndependentOfFlag(AndroidApp input) throws Exception { + private void failsIndependentOfFlag(List<byte[]> input) throws Exception { failsWithOption(input, true); failsWithOption(input, false); } - private void checkPresenceOfCovariantAnnotations(AndroidApp app, boolean expected) + private void checkPresenceOfCovariantAnnotations(List<byte[]> input, boolean expected) throws Exception { - CodeInspector inspector = new CodeInspector(app); + CodeInspector inspector = new CodeInspector(buildAndroidApp(input)); + checkPresenceOfCovariantAnnotations(inspector, expected); + } + + private void checkPresenceOfCovariantAnnotations(CodeInspector inspector, boolean expected) { assertEquals( expected, inspector.allClasses().stream() @@ -196,9 +235,7 @@ .anyMatch(method -> method.annotation(CRTS_TYPE_NAME).isPresent()))); } - private void checkPresenceOfSyntheticMethods(AndroidApp output) throws Exception { - CodeInspector inspector = new CodeInspector(output); - + private void checkPresenceOfSyntheticMethods(CodeInspector inspector) throws Exception { // Get classes A, B, and C. ClassSubject clazzA = inspector.clazz(A.class.getCanonicalName()); assertThat(clazzA, isPresent());
diff --git a/src/test/java/com/android/tools/r8/keepanno/AccessFlagConfig.java b/src/test/java/com/android/tools/r8/keepanno/AccessFlagConfig.java new file mode 100644 index 0000000..5ce7bb5 --- /dev/null +++ b/src/test/java/com/android/tools/r8/keepanno/AccessFlagConfig.java
@@ -0,0 +1,52 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.keepanno; + +import static org.junit.Assert.assertFalse; + +import com.android.tools.r8.keepanno.ast.AnnotationConstants.MemberAccess; +import com.google.common.collect.ImmutableList; +import java.util.List; +import org.objectweb.asm.Opcodes; + +public class AccessFlagConfig { + + public static List<AccessFlagConfig> MEMBER_CONFIGS = + ImmutableList.of( + // Member patterns. + new AccessFlagConfig(MemberAccess.PUBLIC, Opcodes.ACC_PUBLIC), + new AccessFlagConfig(MemberAccess.PROTECTED, Opcodes.ACC_PROTECTED), + new AccessFlagConfig(MemberAccess.PRIVATE, Opcodes.ACC_PRIVATE), + new AccessFlagConfig(MemberAccess.PACKAGE_PRIVATE, 0x0, Opcodes.ACC_PUBLIC), + new AccessFlagConfig(MemberAccess.STATIC, Opcodes.ACC_STATIC), + new AccessFlagConfig(MemberAccess.FINAL, Opcodes.ACC_FINAL), + new AccessFlagConfig(MemberAccess.SYNTHETIC, Opcodes.ACC_SYNTHETIC), + new AccessFlagConfig(MemberAccess.SYNTHETIC, Opcodes.ACC_SYNTHETIC)); + + final String enumValue; + final int positive; + final int negative; + + public AccessFlagConfig(String enumValue, int access) { + this.enumValue = enumValue; + positive = access; + negative = 0x0; + } + + public AccessFlagConfig(String enumValue, int positive, int negative) { + this.enumValue = enumValue; + this.positive = positive; + this.negative = negative; + } + + @Override + public String toString() { + return enumValue; + } + + public AccessFlagConfig invert() { + assertFalse(enumValue.startsWith(MemberAccess.NEGATION_PREFIX)); + return new AccessFlagConfig(MemberAccess.NEGATION_PREFIX + enumValue, negative, positive); + } +}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepAccessFlagsOnFieldsTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepAccessFlagsOnFieldsTest.java new file mode 100644 index 0000000..b48b7c7 --- /dev/null +++ b/src/test/java/com/android/tools/r8/keepanno/KeepAccessFlagsOnFieldsTest.java
@@ -0,0 +1,155 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.keepanno; + +import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.keepanno.annotations.FieldAccessFlags; +import com.android.tools.r8.keepanno.annotations.KeepTarget; +import com.android.tools.r8.keepanno.annotations.UsesReflection; +import com.android.tools.r8.keepanno.ast.AnnotationConstants; +import com.android.tools.r8.keepanno.ast.AnnotationConstants.FieldAccess; +import com.android.tools.r8.transformers.ClassTransformer; +import com.android.tools.r8.transformers.MethodTransformer; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.google.common.collect.ImmutableList; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.Opcodes; + +@RunWith(Parameterized.class) +public class KeepAccessFlagsOnFieldsTest extends TestBase { + + private static final List<AccessFlagConfig> CONFIGS = + ImmutableList.<AccessFlagConfig>builder() + .addAll(AccessFlagConfig.MEMBER_CONFIGS) + .add(new AccessFlagConfig(FieldAccess.VOLATILE, Opcodes.ACC_VOLATILE)) + .add(new AccessFlagConfig(FieldAccess.TRANSIENT, Opcodes.ACC_TRANSIENT)) + .build(); + + @Parameter(0) + public TestParameters parameters; + + @Parameter(1) + public AccessFlagConfig config; + + @Parameterized.Parameters(name = "{0}, {1}") + public static List<Object[]> data() { + List<AccessFlagConfig> configs = new ArrayList<>(CONFIGS.size() * 2); + CONFIGS.forEach( + c -> { + configs.add(c); + configs.add(c.invert()); + }); + return buildParameters( + getTestParameters().withDefaultDexRuntime().withApiLevel(AndroidApiLevel.B).build(), + configs); + } + + @Test + public void testWithRuleExtraction() throws Exception { + testForR8(parameters.getBackend()) + .setMinApi(parameters.getApiLevel()) + .enableExperimentalKeepAnnotations() + .addProgramClassFileData(getTargetClass()) + .addProgramClassFileData(getMainClass()) + .addKeepMainRule(TestClass.class) + .compile() + .inspect(this::checkOutput); + } + + public byte[] getTargetClass() throws Exception { + return transformer(A.class) + .addClassTransformer( + new ClassTransformer() { + @Override + public FieldVisitor visitField( + int access, String name, String descriptor, String signature, Object value) { + if (name.equals("x")) { + access = config.positive; + } + if (name.equals("y")) { + access = config.negative; + } + return super.visitField(access, name, descriptor, signature, value); + } + }) + .transform(); + } + + public byte[] getMainClass() throws Exception { + return transformer(TestClass.class) + .addMethodTransformer( + new MethodTransformer() { + @Override + public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { + assertEquals(AnnotationConstants.UsesReflection.DESCRIPTOR, descriptor); + return new AnnotationVisitor( + ASM_VERSION, super.visitAnnotation(descriptor, visible)) { + @Override + public AnnotationVisitor visitArray(String name) { + assertEquals(AnnotationConstants.UsesReflection.value, name); + return new AnnotationVisitor(ASM_VERSION, super.visitArray(name)) { + @Override + public AnnotationVisitor visitAnnotation(String name, String descriptor) { + assertEquals(AnnotationConstants.Target.DESCRIPTOR, descriptor); + return new AnnotationVisitor( + ASM_VERSION, super.visitAnnotation(name, descriptor)) { + @Override + public AnnotationVisitor visitArray(String name) { + assertEquals(AnnotationConstants.Item.fieldAccess, name); + AnnotationVisitor visitor = super.visitArray(name); + visitor.visitEnum(null, FieldAccess.DESCRIPTOR, config.enumValue); + visitor.visitEnd(); + return null; + } + }; + } + }; + } + }; + } + }) + .transform(); + } + + private void checkOutput(CodeInspector inspector) { + assertThat(inspector.clazz(A.class).uniqueFieldWithOriginalName("x"), isPresent()); + assertThat(inspector.clazz(A.class).uniqueFieldWithOriginalName("y"), isAbsent()); + } + + static class A { + int x; + int y; + } + + static class TestClass { + + @UsesReflection({ + @KeepTarget( + classConstant = A.class, + fieldAccess = {FieldAccessFlags.PUBLIC}) + }) + public static void main(String[] args) throws Exception { + Object o = System.nanoTime() > 0 ? new A() : null; + for (Field f : o.getClass().getDeclaredFields()) { + System.out.println(f.getName()); + } + } + } +}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepAccessFlagsOnMembersTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepAccessFlagsOnMembersTest.java new file mode 100644 index 0000000..d6eb3f2 --- /dev/null +++ b/src/test/java/com/android/tools/r8/keepanno/KeepAccessFlagsOnMembersTest.java
@@ -0,0 +1,180 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.keepanno; + +import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.keepanno.annotations.KeepTarget; +import com.android.tools.r8.keepanno.annotations.MemberAccessFlags; +import com.android.tools.r8.keepanno.annotations.UsesReflection; +import com.android.tools.r8.keepanno.ast.AnnotationConstants; +import com.android.tools.r8.transformers.ClassTransformer; +import com.android.tools.r8.transformers.MethodTransformer; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; + +@RunWith(Parameterized.class) +public class KeepAccessFlagsOnMembersTest extends TestBase { + + @Parameter(0) + public TestParameters parameters; + + @Parameter(1) + public AccessFlagConfig config; + + @Parameterized.Parameters(name = "{0}, {1}") + public static List<Object[]> data() { + List<AccessFlagConfig> configs = new ArrayList<>(AccessFlagConfig.MEMBER_CONFIGS.size() * 2); + AccessFlagConfig.MEMBER_CONFIGS.forEach( + c -> { + configs.add(c); + configs.add(c.invert()); + }); + return buildParameters( + getTestParameters().withDefaultDexRuntime().withApiLevel(AndroidApiLevel.B).build(), + configs); + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .setMinApi(parameters.getApiLevel()) + .enableExperimentalKeepAnnotations() + .addProgramClassFileData(getTargetClass()) + .addProgramClassFileData(getMainClass()) + .addKeepMainRule(TestClass.class) + .compile() + .inspect(this::checkOutput); + } + + public byte[] getTargetClass() throws Exception { + return transformer(A.class) + .addClassTransformer( + new ClassTransformer() { + @Override + public FieldVisitor visitField( + int access, String name, String descriptor, String signature, Object value) { + if (name.equals("x")) { + access = config.positive; + } + if (name.equals("y")) { + access = config.negative; + } + return super.visitField(access, name, descriptor, signature, value); + } + + @Override + public MethodVisitor visitMethod( + int access, + String name, + String descriptor, + String signature, + String[] exceptions) { + if (name.equals("foo")) { + access = config.positive; + } + if (name.equals("bar")) { + access = config.negative; + } + return super.visitMethod(access, name, descriptor, signature, exceptions); + } + }) + .transform(); + } + + public byte[] getMainClass() throws Exception { + return transformer(TestClass.class) + .addMethodTransformer( + new MethodTransformer() { + @Override + public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { + assertEquals(AnnotationConstants.UsesReflection.DESCRIPTOR, descriptor); + return new AnnotationVisitor( + ASM_VERSION, super.visitAnnotation(descriptor, visible)) { + @Override + public AnnotationVisitor visitArray(String name) { + assertEquals(AnnotationConstants.UsesReflection.value, name); + return new AnnotationVisitor(ASM_VERSION, super.visitArray(name)) { + @Override + public AnnotationVisitor visitAnnotation(String name, String descriptor) { + assertEquals(AnnotationConstants.Target.DESCRIPTOR, descriptor); + return new AnnotationVisitor( + ASM_VERSION, super.visitAnnotation(name, descriptor)) { + @Override + public AnnotationVisitor visitArray(String name) { + assertEquals(AnnotationConstants.Item.memberAccess, name); + AnnotationVisitor visitor = super.visitArray(name); + visitor.visitEnum( + null, + AnnotationConstants.MemberAccess.DESCRIPTOR, + config.enumValue); + visitor.visitEnd(); + return null; + } + }; + } + }; + } + }; + } + }) + .transform(); + } + + private void checkOutput(CodeInspector inspector) { + assertThat(inspector.clazz(A.class).uniqueFieldWithOriginalName("x"), isPresent()); + assertThat(inspector.clazz(A.class).uniqueFieldWithOriginalName("y"), isAbsent()); + assertThat(inspector.clazz(A.class).uniqueMethodWithOriginalName("foo"), isPresent()); + assertThat(inspector.clazz(A.class).uniqueMethodWithOriginalName("bar"), isAbsent()); + } + + static class A { + int x; + + int y; + + int foo() { + return 1; + } + + int bar() { + return 2; + } + } + + static class TestClass { + + @UsesReflection({ + @KeepTarget( + classConstant = A.class, + memberAccess = {MemberAccessFlags.PUBLIC}) + }) + public static void main(String[] args) throws Exception { + Object o = System.nanoTime() > 0 ? new A() : null; + for (Field f : o.getClass().getDeclaredFields()) { + System.out.println(f.getName()); + } + for (Method m : o.getClass().getDeclaredMethods()) { + System.out.println(m.getName()); + } + } + } +}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepAccessFlagsOnMethodsTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepAccessFlagsOnMethodsTest.java new file mode 100644 index 0000000..de33e6d --- /dev/null +++ b/src/test/java/com/android/tools/r8/keepanno/KeepAccessFlagsOnMethodsTest.java
@@ -0,0 +1,168 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.keepanno; + +import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.keepanno.annotations.KeepTarget; +import com.android.tools.r8.keepanno.annotations.MethodAccessFlags; +import com.android.tools.r8.keepanno.annotations.UsesReflection; +import com.android.tools.r8.keepanno.ast.AnnotationConstants; +import com.android.tools.r8.keepanno.ast.AnnotationConstants.MethodAccess; +import com.android.tools.r8.transformers.ClassTransformer; +import com.android.tools.r8.transformers.MethodTransformer; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.google.common.collect.ImmutableList; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +@RunWith(Parameterized.class) +public class KeepAccessFlagsOnMethodsTest extends TestBase { + + private static final List<AccessFlagConfig> CONFIGS = + ImmutableList.<AccessFlagConfig>builder() + .addAll(AccessFlagConfig.MEMBER_CONFIGS) + .add(new AccessFlagConfig(MethodAccess.NATIVE, Opcodes.ACC_NATIVE)) + .add(new AccessFlagConfig(MethodAccess.ABSTRACT, Opcodes.ACC_ABSTRACT)) + .add(new AccessFlagConfig(MethodAccess.STRICT_FP, Opcodes.ACC_STRICT)) + .build(); + + @Parameter(0) + public TestParameters parameters; + + @Parameter(1) + public AccessFlagConfig config; + + @Parameterized.Parameters(name = "{0}, {1}") + public static List<Object[]> data() { + List<AccessFlagConfig> configs = new ArrayList<>(CONFIGS.size() * 2); + CONFIGS.forEach( + c -> { + configs.add(c); + configs.add(c.invert()); + }); + return buildParameters( + getTestParameters().withDefaultDexRuntime().withApiLevel(AndroidApiLevel.B).build(), + configs); + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .setMinApi(parameters.getApiLevel()) + .enableExperimentalKeepAnnotations() + .addProgramClassFileData(getTargetClass()) + .addProgramClassFileData(getMainClass()) + .addKeepMainRule(TestClass.class) + .compile() + .inspect(this::checkOutput); + } + + public byte[] getTargetClass() throws Exception { + return transformer(A.class) + .addClassTransformer( + new ClassTransformer() { + @Override + public MethodVisitor visitMethod( + int access, + String name, + String descriptor, + String signature, + String[] exceptions) { + if (name.equals("x")) { + access = config.positive; + } + if (name.equals("y")) { + access = config.negative; + } + return super.visitMethod(access, name, descriptor, signature, exceptions); + } + }) + .transform(); + } + + public byte[] getMainClass() throws Exception { + return transformer(TestClass.class) + .addMethodTransformer( + new MethodTransformer() { + @Override + public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { + assertEquals(AnnotationConstants.UsesReflection.DESCRIPTOR, descriptor); + return new AnnotationVisitor( + ASM_VERSION, super.visitAnnotation(descriptor, visible)) { + @Override + public AnnotationVisitor visitArray(String name) { + assertEquals(AnnotationConstants.UsesReflection.value, name); + return new AnnotationVisitor(ASM_VERSION, super.visitArray(name)) { + @Override + public AnnotationVisitor visitAnnotation(String name, String descriptor) { + assertEquals(AnnotationConstants.Target.DESCRIPTOR, descriptor); + return new AnnotationVisitor( + ASM_VERSION, super.visitAnnotation(name, descriptor)) { + @Override + public AnnotationVisitor visitArray(String name) { + assertEquals(AnnotationConstants.Item.methodAccess, name); + AnnotationVisitor visitor = super.visitArray(name); + visitor.visitEnum( + null, + AnnotationConstants.MethodAccess.DESCRIPTOR, + config.enumValue); + visitor.visitEnd(); + return null; + } + }; + } + }; + } + }; + } + }) + .transform(); + } + + private void checkOutput(CodeInspector inspector) { + assertThat(inspector.clazz(A.class).uniqueMethodWithOriginalName("x"), isPresent()); + assertThat(inspector.clazz(A.class).uniqueMethodWithOriginalName("y"), isAbsent()); + } + + static class A { + int x() { + return 1; + } + + int y() { + return 2; + } + } + + static class TestClass { + + @UsesReflection({ + @KeepTarget( + classConstant = A.class, + methodAccess = {MethodAccessFlags.PUBLIC}) + }) + public static void main(String[] args) throws Exception { + Object o = System.nanoTime() > 0 ? new A() : null; + for (Method m : o.getClass().getDeclaredMethods()) { + System.out.println(m.getName()); + } + } + } +}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepAccessVisibilityFlagsTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepAccessVisibilityFlagsTest.java new file mode 100644 index 0000000..c2d5570 --- /dev/null +++ b/src/test/java/com/android/tools/r8/keepanno/KeepAccessVisibilityFlagsTest.java
@@ -0,0 +1,261 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.keepanno; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.keepanno.annotations.FieldAccessFlags; +import com.android.tools.r8.keepanno.annotations.KeepItemKind; +import com.android.tools.r8.keepanno.annotations.KeepTarget; +import com.android.tools.r8.keepanno.annotations.MemberAccessFlags; +import com.android.tools.r8.keepanno.annotations.MethodAccessFlags; +import com.android.tools.r8.keepanno.annotations.UsesReflection; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class KeepAccessVisibilityFlagsTest extends TestBase { + + static final String EXPECTED = + StringUtils.lines( + // Field targets. + "packagePrivateField", + "protectedField", + "publicField", + // Method targets. + "privateMethod", + "protectedMethod", + "publicMethod", + // Member field targets. + "packagePrivateField", + "privateField", + // Member method targets. + "packagePrivateMethod", + "privateMethod"); + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build(); + } + + public KeepAccessVisibilityFlagsTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testReference() throws Exception { + testForRuntime(parameters) + .addProgramClasses(getInputClasses()) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + } + + @Test + public void testWithRuleExtraction() throws Exception { + testForR8(parameters.getBackend()) + .enableExperimentalKeepAnnotations() + .addProgramClasses(getInputClasses()) + .addKeepMainRule(TestClass.class) + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED) + .inspect(this::checkOutput); + } + + public List<Class<?>> getInputClasses() { + return ImmutableList.of( + TestClass.class, + A.class, + FieldRuleTarget.class, + MethodRuleTarget.class, + MemberRuleTarget.class); + } + + private static List<String> allMembers = + ImmutableList.of( + "publicField", + "protectedField", + "packagePrivateField", + "privateField", + "publicMethod", + "protectedMethod", + "packagePrivateMethod", + "privateMethod"); + + private void checkOutput(CodeInspector inspector) { + assertPresent( + inspector, FieldRuleTarget.class, "publicField", "protectedField", "packagePrivateField"); + assertPresent( + inspector, MethodRuleTarget.class, "publicMethod", "protectedMethod", "privateMethod"); + assertPresent( + inspector, + MemberRuleTarget.class, + "packagePrivateField", + "privateField", + "packagePrivateMethod", + "privateMethod"); + } + + private void assertPresent(CodeInspector inspector, Class<?> clazz, String... members) { + ClassSubject subject = inspector.clazz(clazz); + assertThat(subject, isPresent()); + Set<String> expectedPresent = ImmutableSet.copyOf(members); + for (String member : allMembers) { + if (member.endsWith("Field")) { + assertThat( + subject.uniqueFieldWithOriginalName(member), + expectedPresent.contains(member) ? isPresent() : isAbsent()); + } else { + assertThat( + subject.uniqueMethodWithOriginalName(member), + expectedPresent.contains(member) ? isPresent() : isAbsent()); + } + } + } + + abstract static class FieldRuleTarget { + public String publicField = "public"; + protected String protectedField = "protected"; + private String privateField = "private"; + String packagePrivateField = "package-private"; + + public void publicMethod() {} + + protected void protectedMethod() {} + + private void privateMethod() {} + + void packagePrivateMethod() {} + } + + abstract static class MethodRuleTarget { + public String publicField = "public"; + protected String protectedField = "protected"; + private String privateField = "private"; + String packagePrivateField = "package-private"; + + public void publicMethod() {} + + protected void protectedMethod() {} + + private void privateMethod() {} + + void packagePrivateMethod() {} + } + + abstract static class MemberRuleTarget { + public String publicField = "public"; + protected String protectedField = "protected"; + private String privateField = "private"; + String packagePrivateField = "package-private"; + + public void publicMethod() {} + + protected void protectedMethod() {} + + private void privateMethod() {} + + void packagePrivateMethod() {} + } + + static class A { + + @UsesReflection({ + @KeepTarget( + kind = KeepItemKind.CLASS_AND_MEMBERS, + classConstant = FieldRuleTarget.class, + fieldAccess = {FieldAccessFlags.NON_PRIVATE}), + @KeepTarget( + kind = KeepItemKind.CLASS_AND_MEMBERS, + classConstant = MethodRuleTarget.class, + methodAccess = {MethodAccessFlags.NON_PACKAGE_PRIVATE}), + @KeepTarget( + kind = KeepItemKind.CLASS_AND_MEMBERS, + classConstant = MemberRuleTarget.class, + memberAccess = {MemberAccessFlags.PACKAGE_PRIVATE, MemberAccessFlags.PRIVATE}), + }) + void foo() throws Exception { + // Print all non-private fields. + { + List<String> nonPrivateFields = new ArrayList<>(); + for (Field field : FieldRuleTarget.class.getDeclaredFields()) { + int mod = field.getModifiers(); + if (!Modifier.isPrivate(mod)) { + nonPrivateFields.add(field.getName()); + } + } + printSorted(nonPrivateFields); + } + // Print all non-package-private methods. + { + List<String> nonPackagePrivateMethods = new ArrayList<>(); + for (Method method : MethodRuleTarget.class.getDeclaredMethods()) { + int mod = method.getModifiers(); + if (Modifier.isPublic(mod) || Modifier.isProtected(mod) || Modifier.isPrivate(mod)) { + nonPackagePrivateMethods.add(method.getName()); + } + } + printSorted(nonPackagePrivateMethods); + } + // Print all private and package-private members. + { + List<String> privateOrPackagePrivateFields = new ArrayList<>(); + for (Field field : MemberRuleTarget.class.getDeclaredFields()) { + int mod = field.getModifiers(); + if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod)) { + privateOrPackagePrivateFields.add(field.getName()); + } + } + printSorted(privateOrPackagePrivateFields); + } + { + List<String> privateOrPackagePrivateMethods = new ArrayList<>(); + for (Method method : MemberRuleTarget.class.getDeclaredMethods()) { + int mod = method.getModifiers(); + if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod)) { + privateOrPackagePrivateMethods.add(method.getName()); + } + } + printSorted(privateOrPackagePrivateMethods); + } + } + + // The order of methods and fields is different on stock JDKs depending on linux or windows + // hosts. It is also different once compiled to DEX where the pools are split. Sort the + // names lexicographically to avoid differences in output. + private static void printSorted(List<String> strings) { + strings.sort(String::compareTo); + for (String string : strings) { + System.out.println(string); + } + } + } + + static class TestClass { + + public static void main(String[] args) throws Exception { + new A().foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepClassApiTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepClassApiTest.java new file mode 100644 index 0000000..7c1b0f3 --- /dev/null +++ b/src/test/java/com/android/tools/r8/keepanno/KeepClassApiTest.java
@@ -0,0 +1,134 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.keepanno; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.keepanno.annotations.KeepForApi; +import com.android.tools.r8.keepanno.annotations.MemberAccessFlags; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.google.common.collect.ImmutableList; +import java.nio.file.Path; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class KeepClassApiTest extends TestBase { + + static final String EXPECTED = StringUtils.lines("A::bar", "B::foo"); + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters() + .withDefaultRuntimes() + .withApiLevel(AndroidApiLevel.B) + .enableApiLevelsForCf() + .build(); + } + + public KeepClassApiTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testReference() throws Exception { + testForRuntime(parameters) + .addProgramClasses(getLibraryClasses()) + .addProgramClasses(getClientClasses()) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + } + + @Test + public void testWithRuleExtraction() throws Exception { + Path lib = + testForR8(parameters.getBackend()) + .enableExperimentalKeepAnnotations() + .addProgramClasses(getLibraryClasses()) + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(this::checkLibraryOutput) + .writeToZip(); + + testForD8(parameters.getBackend()) + .addProgramClasses(getClientClasses()) + .addProgramFiles(lib) + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + } + + public List<Class<?>> getLibraryClasses() { + return ImmutableList.of(A.class, B.class); + } + + public List<Class<?>> getClientClasses() { + return ImmutableList.of(TestClass.class); + } + + private void checkLibraryOutput(CodeInspector inspector) { + ClassSubject aClass = inspector.clazz(A.class); + assertThat(aClass, isPresent()); + assertThat(aClass.uniqueMethodWithOriginalName("foo"), isPresent()); + assertThat(aClass.uniqueMethodWithOriginalName("bar"), isPresent()); + assertThat(aClass.uniqueMethodWithOriginalName("baz"), isAbsent()); + ClassSubject bClass = inspector.clazz(B.class); + assertThat(bClass, isPresent()); + assertThat(bClass.uniqueMethodWithOriginalName("foo"), isPresent()); + assertThat(bClass.uniqueMethodWithOriginalName("bar"), isAbsent()); + assertThat(bClass.uniqueMethodWithOriginalName("baz"), isAbsent()); + } + + @KeepForApi /* if members are unspecified then default is any public or protected member. */ + public static class A { + + public void foo() { + System.out.println("A::foo"); + } + + protected void bar() { + System.out.println("A::bar"); + } + + void baz() { + System.out.println("A::baz"); + } + } + + @KeepForApi(memberAccess = {MemberAccessFlags.PUBLIC}) + public static class B { + + public void foo() { + System.out.println("B::foo"); + } + + protected void bar() { + System.out.println("B::bar"); + } + + void baz() { + System.out.println("B::baz"); + } + } + + static class TestClass { + + public static void main(String[] args) throws Exception { + new A().bar(); + new B().foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepFieldValueApiTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepFieldValueApiTest.java new file mode 100644 index 0000000..00d7d28 --- /dev/null +++ b/src/test/java/com/android/tools/r8/keepanno/KeepFieldValueApiTest.java
@@ -0,0 +1,124 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.keepanno; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.keepanno.annotations.KeepForApi; +import com.android.tools.r8.keepanno.annotations.KeepItemKind; +import com.android.tools.r8.keepanno.annotations.KeepTarget; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.google.common.collect.ImmutableList; +import java.nio.file.Path; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class KeepFieldValueApiTest extends TestBase { + + static final String EXPECTED = StringUtils.lines("B::foo"); + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters() + .withDefaultRuntimes() + .withApiLevel(AndroidApiLevel.B) + .enableApiLevelsForCf() + .build(); + } + + public KeepFieldValueApiTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testReference() throws Exception { + testForRuntime(parameters) + .addProgramClasses(getLibraryClasses()) + .addProgramClasses(getClientClasses()) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + } + + @Test + public void testWithRuleExtraction() throws Exception { + Path lib = + testForR8(parameters.getBackend()) + .enableExperimentalKeepAnnotations() + .addProgramClasses(getLibraryClasses()) + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(this::checkLibraryOutput) + .writeToZip(); + + testForD8(parameters.getBackend()) + .addProgramClasses(getClientClasses()) + .addProgramFiles(lib) + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + } + + public List<Class<?>> getLibraryClasses() { + return ImmutableList.of(A.class, B.class); + } + + public List<Class<?>> getClientClasses() { + return ImmutableList.of(TestClass.class); + } + + private void checkLibraryOutput(CodeInspector inspector) { + ClassSubject aClass = inspector.clazz(A.class); + assertThat(aClass, isPresent()); + assertThat(aClass.uniqueFieldWithFinalName("CLASS"), isPresent()); + ClassSubject bClass = inspector.clazz(B.class); + assertThat(bClass, isPresent()); + assertThat(bClass.uniqueMethodWithOriginalName("foo"), isPresent()); + assertThat(bClass.uniqueMethodWithOriginalName("bar"), isPresent()); + assertThat(bClass.uniqueMethodWithOriginalName("baz"), isPresent()); + } + + public static class A { + + @KeepForApi( + additionalTargets = { + @KeepTarget(classConstant = B.class, kind = KeepItemKind.CLASS_AND_MEMBERS) + }) + public static final String CLASS = "com.android.tools.r8.keepanno.KeepFieldValueApiTest$B"; + } + + public static class B { + + public void foo() { + System.out.println("B::foo"); + } + + protected void bar() { + System.out.println("B::bar"); + } + + void baz() { + System.out.println("B::baz"); + } + } + + static class TestClass { + + public static void main(String[] args) throws Exception { + B b = (B) Class.forName(A.CLASS).getConstructor().newInstance(); + b.foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepInvalidForApiTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepInvalidForApiTest.java new file mode 100644 index 0000000..6cb2ddd --- /dev/null +++ b/src/test/java/com/android/tools/r8/keepanno/KeepInvalidForApiTest.java
@@ -0,0 +1,116 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.keepanno; + +import static org.hamcrest.CoreMatchers.allOf; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.fail; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.keepanno.annotations.KeepForApi; +import com.android.tools.r8.keepanno.annotations.MemberAccessFlags; +import com.android.tools.r8.keepanno.asm.KeepEdgeReader; +import com.android.tools.r8.keepanno.ast.KeepEdge; +import com.android.tools.r8.keepanno.ast.KeepEdgeException; +import com.android.tools.r8.keepanno.keeprules.KeepRuleExtractor; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import org.hamcrest.Matcher; +import org.junit.Test; +import org.junit.function.ThrowingRunnable; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class KeepInvalidForApiTest extends TestBase { + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withNoneRuntime().build(); + } + + public KeepInvalidForApiTest(TestParameters parameters) { + parameters.assertNoneRuntime(); + } + + private static List<String> extractRuleForClass(Class<?> clazz) throws IOException { + Set<KeepEdge> keepEdges = KeepEdgeReader.readKeepEdges(ToolHelper.getClassAsBytes(clazz)); + List<String> rules = new ArrayList<>(); + KeepRuleExtractor extractor = new KeepRuleExtractor(rules::add); + keepEdges.forEach(extractor::extract); + return rules; + } + + private void assertThrowsWith(ThrowingRunnable fn, Matcher<String> matcher) { + try { + fn.run(); + } catch (KeepEdgeException e) { + assertThat(e.getMessage(), matcher); + return; + } catch (Throwable e) { + fail("Expected run to fail with KeepEdgeException, but failed with: " + e); + } + fail("Expected run to fail"); + } + + @Test + public void testInvalidMemberAccess() throws Exception { + assertThrowsWith( + () -> extractRuleForClass(RefineMemberAccess.class), + allOf( + containsString("Unexpected array"), + containsString("@KeepForApi"), + containsString("memberAccess"))); + } + + static class RefineMemberAccess { + + @KeepForApi(memberAccess = {MemberAccessFlags.PUBLIC}) + public static void main(String[] args) throws Exception { + System.out.println("Hello, world"); + } + } + + @Test + public void testInvalidMethodName() throws Exception { + assertThrowsWith( + () -> extractRuleForClass(RefineMethodName.class), + allOf( + containsString("Unexpected value"), + containsString("@KeepForApi"), + containsString("methodName"))); + } + + static class RefineMethodName { + + @KeepForApi(methodName = "foo") + public static void main(String[] args) throws Exception { + System.out.println("Hello, world"); + } + } + + @Test + public void testInvalidFieldName() throws Exception { + assertThrowsWith( + () -> extractRuleForClass(RefineFieldName.class), + allOf( + containsString("Unexpected value"), + containsString("@KeepForApi"), + containsString("fieldName"))); + } + + static class RefineFieldName { + + @KeepForApi(fieldName = "foo") + public static void main(String[] args) throws Exception { + System.out.println("Hello, world"); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepMembersAccessFlagsTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepMembersAccessFlagsTest.java new file mode 100644 index 0000000..c071aab --- /dev/null +++ b/src/test/java/com/android/tools/r8/keepanno/KeepMembersAccessFlagsTest.java
@@ -0,0 +1,121 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.keepanno; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.keepanno.annotations.KeepTarget; +import com.android.tools.r8.keepanno.annotations.MemberAccessFlags; +import com.android.tools.r8.keepanno.annotations.UsesReflection; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.google.common.collect.ImmutableList; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class KeepMembersAccessFlagsTest extends TestBase { + + static final String EXPECTED = StringUtils.lines("Hello, world", "bar"); + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build(); + } + + public KeepMembersAccessFlagsTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testReference() throws Exception { + testForRuntime(parameters) + .addProgramClasses(getInputClasses()) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + } + + @Test + public void testWithRuleExtraction() throws Exception { + testForR8(parameters.getBackend()) + .enableExperimentalKeepAnnotations() + .addProgramClasses(getInputClasses()) + .addKeepMainRule(TestClass.class) + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED) + .inspect(this::checkOutput); + } + + public List<Class<?>> getInputClasses() { + return ImmutableList.of(TestClass.class, A.class); + } + + private void checkOutput(CodeInspector inspector) { + ClassSubject clazz = inspector.clazz(A.class); + assertThat(clazz, isPresent()); + assertThat(clazz.uniqueFieldWithOriginalName("staticField"), isAbsent()); + assertThat(clazz.uniqueFieldWithOriginalName("fieldA"), isPresent()); + assertThat(clazz.uniqueFieldWithOriginalName("fieldB"), isAbsent()); + assertThat(clazz.uniqueMethodWithOriginalName("bar"), isPresent()); + assertThat(clazz.uniqueMethodWithOriginalName("baz"), isAbsent()); + assertThat(clazz.uniqueMethodWithOriginalName("foobar"), isAbsent()); + } + + static class A { + + public static String staticField = "the static"; + + public String fieldA = "Hello, world"; + + private Integer fieldB = 42; + + @UsesReflection({ + @KeepTarget( + classConstant = A.class, + memberAccess = {MemberAccessFlags.PUBLIC, MemberAccessFlags.NON_STATIC}) + }) + void foo() throws Exception { + for (Field field : getClass().getDeclaredFields()) { + int modifiers = field.getModifiers(); + if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) { + System.out.println(field.get(this)); + } + } + for (Method method : getClass().getDeclaredMethods()) { + int modifiers = method.getModifiers(); + if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) { + System.out.println(method.getName()); + } + } + } + + public void bar() {} + + private void baz() {} + + public static void foobar() {} + } + + static class TestClass { + + public static void main(String[] args) throws Exception { + new A().foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepMembersApiTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepMembersApiTest.java new file mode 100644 index 0000000..b8169ba --- /dev/null +++ b/src/test/java/com/android/tools/r8/keepanno/KeepMembersApiTest.java
@@ -0,0 +1,141 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.keepanno; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.keepanno.annotations.KeepForApi; +import com.android.tools.r8.keepanno.annotations.KeepItemKind; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.google.common.collect.ImmutableList; +import java.nio.file.Path; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class KeepMembersApiTest extends TestBase { + + static final String EXPECTED = StringUtils.lines("A::bar", "B::foo"); + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters() + .withDefaultRuntimes() + .withApiLevel(AndroidApiLevel.B) + .enableApiLevelsForCf() + .build(); + } + + public KeepMembersApiTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testReference() throws Exception { + testForRuntime(parameters) + .addProgramClasses(getLibraryClasses()) + .addProgramClasses(getClientClasses()) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + } + + @Test + public void testWithRuleExtraction() throws Exception { + Path lib = + testForR8(parameters.getBackend()) + .enableExperimentalKeepAnnotations() + .addProgramClasses(getLibraryClasses()) + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect(this::checkLibraryOutput) + .writeToZip(); + + testForD8(parameters.getBackend()) + .addProgramClasses(getClientClasses()) + .addProgramFiles(lib) + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + } + + public List<Class<?>> getLibraryClasses() { + return ImmutableList.of(A.class, B.class); + } + + public List<Class<?>> getClientClasses() { + return ImmutableList.of(TestClass.class); + } + + private void checkLibraryOutput(CodeInspector inspector) { + ClassSubject aClass = inspector.clazz(A.class); + assertThat(aClass, isPresent()); + assertThat(aClass.uniqueMethodWithOriginalName("foo"), isAbsent()); + assertThat(aClass.uniqueMethodWithOriginalName("bar"), isPresent()); + assertThat(aClass.uniqueMethodWithOriginalName("baz"), isAbsent()); + ClassSubject bClass = inspector.clazz(B.class); + assertThat(bClass, isPresent()); + assertThat(bClass.uniqueMethodWithOriginalName("foo"), isPresent()); + assertThat(bClass.uniqueMethodWithOriginalName("bar"), isAbsent()); + assertThat(bClass.uniqueMethodWithOriginalName("baz"), isAbsent()); + } + + @KeepForApi(kind = KeepItemKind.ONLY_CLASS) + public static class A { + + @KeepForApi + public A() {} + + public void foo() { + System.out.println("A::foo"); + } + + @KeepForApi + protected void bar() { + System.out.println("A::bar"); + } + + void baz() { + System.out.println("A::baz"); + } + } + + public static class B { + + @KeepForApi + public B() {} + + @KeepForApi + public void foo() { + System.out.println("B::foo"); + } + + protected void bar() { + System.out.println("B::bar"); + } + + void baz() { + System.out.println("B::baz"); + } + } + + static class TestClass { + + public static void main(String[] args) throws Exception { + new A().bar(); + new B().foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepMethodsAccessFlagsTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepMethodsAccessFlagsTest.java new file mode 100644 index 0000000..929eb99 --- /dev/null +++ b/src/test/java/com/android/tools/r8/keepanno/KeepMethodsAccessFlagsTest.java
@@ -0,0 +1,118 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.keepanno; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.keepanno.annotations.KeepItemKind; +import com.android.tools.r8.keepanno.annotations.KeepTarget; +import com.android.tools.r8.keepanno.annotations.MethodAccessFlags; +import com.android.tools.r8.keepanno.annotations.UsesReflection; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.google.common.collect.ImmutableList; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class KeepMethodsAccessFlagsTest extends TestBase { + + static final String EXPECTED = StringUtils.lines("hello", "world"); + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build(); + } + + public KeepMethodsAccessFlagsTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testReference() throws Exception { + testForRuntime(parameters) + .addProgramClasses(getInputClasses()) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + } + + @Test + public void testWithRuleExtraction() throws Exception { + testForR8(parameters.getBackend()) + .enableExperimentalKeepAnnotations() + .addProgramClasses(getInputClasses()) + .addKeepMainRule(TestClass.class) + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED) + .inspect(this::checkOutput); + } + + public List<Class<?>> getInputClasses() { + return ImmutableList.of(TestClass.class, A.class, Abs.class); + } + + private void checkOutput(CodeInspector inspector) { + ClassSubject clazz = inspector.clazz(Abs.class); + assertThat(clazz, isPresent()); + assertThat(clazz.uniqueMethodWithOriginalName("hello"), isPresent()); + assertThat(clazz.uniqueMethodWithOriginalName("my"), isAbsent()); + assertThat(clazz.uniqueMethodWithOriginalName("old"), isAbsent()); + assertThat(clazz.uniqueMethodWithOriginalName("world"), isPresent()); + } + + abstract static class Abs { + public abstract void hello(); + + public void my() {} + + abstract void old(); + + public abstract void world(); + } + + static class A { + + @UsesReflection({ + @KeepTarget( + kind = KeepItemKind.CLASS_AND_MEMBERS, + classConstant = Abs.class, + methodAccess = {MethodAccessFlags.PUBLIC, MethodAccessFlags.ABSTRACT}) + }) + void foo() throws Exception { + List<String> sorted = new ArrayList<>(); + for (Method method : Abs.class.getDeclaredMethods()) { + int modifiers = method.getModifiers(); + if (Modifier.isPublic(modifiers) && Modifier.isAbstract(modifiers)) { + sorted.add(method.getName()); + } + } + sorted.sort(String::compareTo); + for (String string : sorted) { + System.out.println(string); + } + } + } + + static class TestClass { + + public static void main(String[] args) throws Exception { + new A().foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepMethodsEmptyAccessFlagsTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepMethodsEmptyAccessFlagsTest.java new file mode 100644 index 0000000..ac720ac --- /dev/null +++ b/src/test/java/com/android/tools/r8/keepanno/KeepMethodsEmptyAccessFlagsTest.java
@@ -0,0 +1,118 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.keepanno; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.keepanno.annotations.KeepItemKind; +import com.android.tools.r8.keepanno.annotations.KeepTarget; +import com.android.tools.r8.keepanno.annotations.UsesReflection; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.google.common.collect.ImmutableList; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class KeepMethodsEmptyAccessFlagsTest extends TestBase { + + static final String EXPECTED = StringUtils.lines("hello", "world"); + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build(); + } + + public KeepMethodsEmptyAccessFlagsTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testReference() throws Exception { + testForRuntime(parameters) + .addProgramClasses(getInputClasses()) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + } + + @Test + public void testWithRuleExtraction() throws Exception { + testForR8(parameters.getBackend()) + .enableExperimentalKeepAnnotations() + .addProgramClasses(getInputClasses()) + .addKeepMainRule(TestClass.class) + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED) + .inspect(this::checkOutput); + } + + public List<Class<?>> getInputClasses() { + return ImmutableList.of(TestClass.class, A.class, Abs.class); + } + + private void checkOutput(CodeInspector inspector) { + ClassSubject clazz = inspector.clazz(Abs.class); + assertThat(clazz, isPresent()); + assertThat(clazz.uniqueMethodWithOriginalName("hello"), isPresent()); + assertThat(clazz.uniqueMethodWithOriginalName("my"), isPresent()); + assertThat(clazz.uniqueMethodWithOriginalName("old"), isPresent()); + assertThat(clazz.uniqueMethodWithOriginalName("world"), isPresent()); + } + + abstract static class Abs { + public abstract void hello(); + + public void my() {} + + abstract void old(); + + public abstract void world(); + } + + static class A { + + @UsesReflection({ + @KeepTarget( + kind = KeepItemKind.CLASS_AND_MEMBERS, + classConstant = Abs.class, + methodAccess = { + /* the explicit empty set matches all access */ + }) + }) + void foo() throws Exception { + List<String> sorted = new ArrayList<>(); + for (Method method : Abs.class.getDeclaredMethods()) { + int modifiers = method.getModifiers(); + if (Modifier.isPublic(modifiers) && Modifier.isAbstract(modifiers)) { + sorted.add(method.getName()); + } + } + sorted.sort(String::compareTo); + for (String string : sorted) { + System.out.println(string); + } + } + } + + static class TestClass { + + public static void main(String[] args) throws Exception { + new A().foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteEnumTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteEnumTest.java index b7c8c16..2d90a08 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteEnumTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteEnumTest.java
@@ -68,13 +68,17 @@ public void testKotlincFailsRenamed() throws Exception { R8TestCompileResult r8libResult = testForR8(parameters.getBackend()) - .addProgramFiles(jarMap.getForConfiguration(kotlinc, targetVersion)) + .addProgramFiles( + jarMap.getForConfiguration(kotlinc, targetVersion), + kotlinc.getKotlinAnnotationJar()) .addClasspathFiles(kotlinc.getKotlinStdlibJar()) .addKeepKotlinMetadata() .addKeepEnumsRule() .addKeepClassRules(DIRECTION_TYPE_NAME) .addKeepClassAndMembersRulesWithAllowObfuscation(DIRECTION_TYPE_NAME) + .allowDiagnosticWarningMessages() .compile() + .apply(KotlinMetadataTestBase::verifyExpectedWarningsFromKotlinReflectAndStdLib) .inspect( inspector -> { ClassSubject direction = inspector.clazz(DIRECTION_TYPE_NAME); @@ -96,13 +100,17 @@ public void testR8() throws Exception { R8TestCompileResult r8libResult = testForR8(parameters.getBackend()) - .addProgramFiles(jarMap.getForConfiguration(kotlinc, targetVersion)) + .addProgramFiles( + jarMap.getForConfiguration(kotlinc, targetVersion), + kotlinc.getKotlinAnnotationJar()) .addClasspathFiles(kotlinc.getKotlinStdlibJar()) .addKeepKotlinMetadata() .addKeepEnumsRule() .addKeepClassRules(DIRECTION_TYPE_NAME) .addKeepClassAndMembersRulesWithAllowObfuscation(DIRECTION_TYPE_NAME) + .allowDiagnosticWarningMessages() .compile() + .apply(KotlinMetadataTestBase::verifyExpectedWarningsFromKotlinReflectAndStdLib) .inspect( inspector -> { ClassSubject direction = inspector.clazz(DIRECTION_TYPE_NAME);
diff --git a/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java b/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java index ab2f10d..89c268e 100644 --- a/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
@@ -106,10 +106,11 @@ .compile() .assertNoErrorMessages() // -keepattributes Signature is added in kotlin-reflect from version 1.4.20. + .applyIf(kotlinParameters.is(KOTLINC_1_3_72), TestCompileResult::assertNoInfoMessages) + // TODO(b/269794485): Figure out why generic signatures fail using kotlin-dev. .applyIf( - kotlinParameters.getCompiler().isNot(KOTLINC_1_3_72), - TestBase::verifyAllInfoFromGenericSignatureTypeParameterValidation, - TestCompileResult::assertNoInfoMessages) + kotlinParameters.getCompiler().isNot(KOTLINC_1_3_72) && !kotlinParameters.isKotlinDev(), + TestBase::verifyAllInfoFromGenericSignatureTypeParameterValidation) .apply(KotlinMetadataTestBase::verifyExpectedWarningsFromKotlinReflectAndStdLib) .writeToZip(foo.toPath()) .run(parameters.getRuntime(), PKG + ".SimpleReflectKt")
diff --git a/src/test/java/com/android/tools/r8/kotlin/reflection/ReflectiveConstructionWithInlineClassTest.java b/src/test/java/com/android/tools/r8/kotlin/reflection/ReflectiveConstructionWithInlineClassTest.java index 6273755..9eecfaf 100644 --- a/src/test/java/com/android/tools/r8/kotlin/reflection/ReflectiveConstructionWithInlineClassTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/reflection/ReflectiveConstructionWithInlineClassTest.java
@@ -102,7 +102,7 @@ .addKeepClassAndMembersRules(PKG + ".Data") // TODO(b/242158616): Figure out why this is necessary. .applyIf( - kotlinc.getCompilerVersion().isGreaterThan(KotlinCompilerVersion.KOTLINC_1_7_0), + kotlinc.is(KotlinCompilerVersion.KOTLINC_1_8_0), b -> b.addKeepClassAndMembersRules( "kotlin.reflect.jvm.internal.ClassValueCache$initClassValue$1")) @@ -119,7 +119,11 @@ .assertNoErrorMessages() .apply(KotlinMetadataTestBase::verifyExpectedWarningsFromKotlinReflectAndStdLib) .run(parameters.getRuntime(), MAIN_CLASS) - .assertFailureWithErrorThatThrows(IllegalArgumentException.class); + // TODO(b/269792580): Figure out why this is throwing an abstract method error. + .assertFailureWithErrorThatThrows( + kotlinParameters.isKotlinDev() && parameters.isCfRuntime() + ? AbstractMethodError.class + : IllegalArgumentException.class); } @Test @@ -130,6 +134,9 @@ .assertNoErrorMessages() .apply(KotlinMetadataTestBase::verifyExpectedWarningsFromKotlinReflectAndStdLib) .run(parameters.getRuntime(), MAIN_CLASS) - .assertSuccessWithOutputLines(EXPECTED_OUTPUT); + // TODO(b/269792580): Figure out why this is throwing an abstract method error. + .assertFailureWithErrorThatThrowsIf( + kotlinParameters.isKotlinDev() && parameters.isCfRuntime(), AbstractMethodError.class) + .assertSuccessWithOutputLinesIf(!kotlinParameters.isKotlinDev(), EXPECTED_OUTPUT); } }
diff --git a/src/test/java/com/android/tools/r8/lightir/LIRBasicCallbackTest.java b/src/test/java/com/android/tools/r8/lightir/LirBasicCallbackTest.java similarity index 81% rename from src/test/java/com/android/tools/r8/lightir/LIRBasicCallbackTest.java rename to src/test/java/com/android/tools/r8/lightir/LirBasicCallbackTest.java index c8e9e8a..59dc2b0 100644 --- a/src/test/java/com/android/tools/r8/lightir/LIRBasicCallbackTest.java +++ b/src/test/java/com/android/tools/r8/lightir/LirBasicCallbackTest.java
@@ -21,14 +21,14 @@ import org.junit.runners.Parameterized; @RunWith(Parameterized.class) -public class LIRBasicCallbackTest extends TestBase { +public class LirBasicCallbackTest extends TestBase { @Parameterized.Parameters(name = "{0}") public static TestParametersCollection data() { return getTestParameters().withNoneRuntime().build(); } - public LIRBasicCallbackTest(TestParameters parameters) { + public LirBasicCallbackTest(TestParameters parameters) { parameters.assertNoneRuntime(); } @@ -36,8 +36,8 @@ public void test() throws Exception { DexItemFactory factory = new DexItemFactory(); DexMethod method = factory.createMethod(Reference.methodFromDescriptor("LFoo;", "bar", "()V")); - LIRCode code = - LIRCode.builder( + LirCode code = + LirCode.builder( method, v -> { throw new Unreachable(); @@ -51,16 +51,16 @@ .addConstInt(42) .build(); - LIRIterator it = code.iterator(); + LirIterator it = code.iterator(); // The iterator and the elements are the same object providing a view on the byte stream. assertTrue(it.hasNext()); - LIRInstructionView next = it.next(); + LirInstructionView next = it.next(); assertSame(it, next); it.accept( insn -> { - assertEquals(LIROpcodes.ACONST_NULL, insn.getOpcode()); + assertEquals(LirOpcodes.ACONST_NULL, insn.getOpcode()); assertEquals(0, insn.getRemainingOperandSizeInBytes()); }); @@ -68,21 +68,21 @@ it.next(); it.accept( insn -> { - assertEquals(LIROpcodes.ICONST, insn.getOpcode()); + assertEquals(LirOpcodes.ICONST, insn.getOpcode()); assertEquals(4, insn.getRemainingOperandSizeInBytes()); }); assertFalse(it.hasNext()); // The iterator can also be use in a normal java for-each loop. // However, the item is not an actual item just a current view, so it can't be cached! - LIRInstructionView oldView = null; - for (LIRInstructionView view : code) { + LirInstructionView oldView = null; + for (LirInstructionView view : code) { if (oldView == null) { oldView = view; - view.accept(insn -> assertEquals(LIROpcodes.ACONST_NULL, insn.getOpcode())); + view.accept(insn -> assertEquals(LirOpcodes.ACONST_NULL, insn.getOpcode())); } else { assertSame(oldView, view); - view.accept(insn -> assertEquals(LIROpcodes.ICONST, insn.getOpcode())); + view.accept(insn -> assertEquals(LirOpcodes.ICONST, insn.getOpcode())); } } }
diff --git a/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java b/src/test/java/com/android/tools/r8/lightir/LirRoundtripTest.java similarity index 92% rename from src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java rename to src/test/java/com/android/tools/r8/lightir/LirRoundtripTest.java index 2188ba6..a796311 100644 --- a/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java +++ b/src/test/java/com/android/tools/r8/lightir/LirRoundtripTest.java
@@ -15,7 +15,7 @@ import org.junit.runners.Parameterized; @RunWith(Parameterized.class) -public class LIRRoundtripTest extends DebugTestBase { +public class LirRoundtripTest extends DebugTestBase { static class TestClass { public static void main(String[] args) { @@ -36,7 +36,7 @@ private final TestParameters parameters; - public LIRRoundtripTest(TestParameters parameters) { + public LirRoundtripTest(TestParameters parameters) { this.parameters = parameters; } @@ -58,7 +58,7 @@ .addOptionsModification( o -> { o.testing.forceIRForCfToCfDesugar = true; - o.testing.roundtripThroughLIR = true; + o.testing.roundtripThroughLir = true; }) .run(parameters.getRuntime(), TestClass.class) .assertSuccessWithOutputLines("Hello, world!"); @@ -73,7 +73,7 @@ .addOptionsModification( o -> { o.testing.forceIRForCfToCfDesugar = true; - o.testing.roundtripThroughLIR = true; + o.testing.roundtripThroughLir = true; }) .run(parameters.getRuntime(), TestClass.class) .assertSuccessWithOutputLines("Hello, world!")
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/CompletenessTestingEnabledTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/CompletenessTestingEnabledTest.java new file mode 100644 index 0000000..d5dc8fa --- /dev/null +++ b/src/test/java/com/android/tools/r8/profile/art/completeness/CompletenessTestingEnabledTest.java
@@ -0,0 +1,50 @@ +// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.profile.art.completeness; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.profile.art.ArtProfileOptions; +import com.android.tools.r8.utils.InternalOptions; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class CompletenessTestingEnabledTest extends TestBase { + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withNoneRuntime().build(); + } + + public CompletenessTestingEnabledTest(TestParameters parameters) { + parameters.assertNoneRuntime(); + } + + /** + * Verifies that -Dcom.android.tools.r8.artprofilerewritingcompletenesscheck=true when running + * from test.py. If running this locally in IntelliJ make sure to set the system property in the + * run configuration. + */ + @Test + public void test() { + // Verify that completeness testing is enabled for testing. + assertEquals("true", System.getProperty(ArtProfileOptions.COMPLETENESS_PROPERTY_KEY, "false")); + assertTrue(new InternalOptions().getArtProfileOptions().isCompletenessCheckForTestingEnabled()); + + // Verify that completeness testing is disabled by default. + System.clearProperty(ArtProfileOptions.COMPLETENESS_PROPERTY_KEY); + assertFalse( + new InternalOptions().getArtProfileOptions().isCompletenessCheckForTestingEnabled()); + System.setProperty(ArtProfileOptions.COMPLETENESS_PROPERTY_KEY, "true"); + } +}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/EnumUnboxingUtilityMethodProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/EnumUnboxingUtilityMethodProfileRewritingTest.java index e9058d4..e74465b 100644 --- a/src/test/java/com/android/tools/r8/profile/art/completeness/EnumUnboxingUtilityMethodProfileRewritingTest.java +++ b/src/test/java/com/android/tools/r8/profile/art/completeness/EnumUnboxingUtilityMethodProfileRewritingTest.java
@@ -98,7 +98,8 @@ assertThat(sharedValuesMethodSubject, isPresent()); profileInspector - .assertContainsClassRule(enumUnboxingSharedUtilityClassSubject) + .assertContainsClassRules( + enumUnboxingLocalUtilityClassSubject, enumUnboxingSharedUtilityClassSubject) .assertContainsMethodRules( mainClassSubject.mainMethod(), localGreetMethodSubject,
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/MovedStaticInterfaceMethodProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/MovedStaticInterfaceMethodProfileRewritingTest.java index 6e7ab5b..009966f 100644 --- a/src/test/java/com/android/tools/r8/profile/art/completeness/MovedStaticInterfaceMethodProfileRewritingTest.java +++ b/src/test/java/com/android/tools/r8/profile/art/completeness/MovedStaticInterfaceMethodProfileRewritingTest.java
@@ -4,6 +4,8 @@ package com.android.tools.r8.profile.art.completeness; +import static com.android.tools.r8.synthesis.SyntheticItemsTestUtils.syntheticCompanionClass; +import static com.android.tools.r8.synthesis.SyntheticItemsTestUtils.syntheticStaticInterfaceMethodAsCompanionMethod; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.MatcherAssert.assertThat; @@ -14,7 +16,6 @@ import com.android.tools.r8.profile.art.model.ExternalArtProfile; import com.android.tools.r8.profile.art.utils.ArtProfileInspector; import com.android.tools.r8.references.Reference; -import com.android.tools.r8.synthesis.SyntheticItemsTestUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.MethodSubject; @@ -48,6 +49,20 @@ } @Test + public void testD8FromProfileAfterDesugaring() throws Exception { + testForD8(parameters.getBackend()) + .addInnerClasses(getClass()) + .addArtProfileForRewriting( + getArtProfileAfterDesugaring( + parameters.canUseDefaultAndStaticInterfaceMethodsWhenDesugaring())) + .setMinApi(parameters.getApiLevel()) + .compile() + .inspectResidualArtProfile(this::inspectD8) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines("Hello, world!"); + } + + @Test public void testR8() throws Exception { parameters.assumeR8TestParameters(); testForR8(parameters.getBackend()) @@ -62,12 +77,42 @@ .assertSuccessWithOutputLines("Hello, world!"); } + @Test + public void testR8FromProfileAfterDesugaring() throws Exception { + parameters.assumeR8TestParameters(); + testForR8(parameters.getBackend()) + .addInnerClasses(getClass()) + .addKeepMainRule(Main.class) + .addArtProfileForRewriting( + getArtProfileAfterDesugaring( + parameters.isCfRuntime() || parameters.canUseDefaultAndStaticInterfaceMethods())) + .enableInliningAnnotations() + .setMinApi(parameters.getApiLevel()) + .compile() + .inspectResidualArtProfile(this::inspectR8) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines("Hello, world!"); + } + private ExternalArtProfile getArtProfile() throws Exception { return ExternalArtProfile.builder() .addMethodRule(Reference.methodFromMethod(I.class.getDeclaredMethod("m"))) .build(); } + private ExternalArtProfile getArtProfileAfterDesugaring( + boolean canUseDefaultAndStaticInterfaceMethods) throws Exception { + if (canUseDefaultAndStaticInterfaceMethods) { + return getArtProfile(); + } else { + return ExternalArtProfile.builder() + .addClassRule(syntheticCompanionClass(I.class)) + .addMethodRule( + syntheticStaticInterfaceMethodAsCompanionMethod(I.class.getDeclaredMethod("m"))) + .build(); + } + } + private void inspectD8(ArtProfileInspector profileInspector, CodeInspector inspector) throws Exception { inspect( @@ -100,8 +145,7 @@ .assertContainsMethodRule(staticInterfaceMethodSubject) .assertContainsNoOtherRules(); } else { - ClassSubject companionClassSubject = - inspector.clazz(SyntheticItemsTestUtils.syntheticCompanionClass(I.class)); + ClassSubject companionClassSubject = inspector.clazz(syntheticCompanionClass(I.class)); assertThat(companionClassSubject, isPresent()); MethodSubject staticInterfaceMethodSubject =
diff --git a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesR8SpecificTest.java b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesR8SpecificTest.java index c3fd851..419ebfc 100644 --- a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesR8SpecificTest.java +++ b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesR8SpecificTest.java
@@ -226,13 +226,16 @@ .filter( allOf( diagnosticMessage(containsString("Running R8 version main")), - diagnosticMessage(containsString("Using version 8.1.0 for"))) + diagnosticMessage( + containsString( + "Using an artificial version newer than any known" + + " version"))) ::matches) .count())) .inspectProguardConfiguration( configuration -> assertEquals( - StringUtils.lines(EXPECTED_A.trim(), EXPECTED_B.trim(), EXPECTED_C.trim()), + StringUtils.lines(EXPECTED_A.trim(), EXPECTED_B.trim(), EXPECTED_E.trim()), configuration.toString())); }
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java index 478af32..e69bb0f 100644 --- a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java +++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -228,6 +228,14 @@ originalMethod.getMethodDescriptor()); } + public static MethodReference syntheticStaticInterfaceMethodAsCompanionMethod(Method method) { + MethodReference originalMethod = Reference.methodFromMethod(method); + ClassReference companionClassReference = + syntheticCompanionClass(originalMethod.getHolderClass()); + return Reference.methodFromDescriptor( + companionClassReference, method.getName(), originalMethod.getMethodDescriptor()); + } + public static ClassReference syntheticEnumUnboxingLocalUtilityClass(Class<?> clazz) { return Reference.classFromTypeName( clazz.getTypeName() + naming.ENUM_UNBOXING_LOCAL_UTILITY_CLASS.getDescriptor());
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticMarkerDexTest.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticMarkerDexTest.java index 5b00775..fd37907 100644 --- a/src/test/java/com/android/tools/r8/synthesis/SyntheticMarkerDexTest.java +++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticMarkerDexTest.java
@@ -68,7 +68,7 @@ DexAnnotation[] annotations = clazz.getDexProgramClass().annotations().annotations; assertEquals(1, annotations.length); DexEncodedAnnotation annotation = annotations[0].annotation; - assertEquals(2, annotation.elements.length); + assertEquals(3, annotation.elements.length); assertEquals( "com.android.tools.r8.annotations.SynthesizedClassV2", annotation.type.toSourceString());
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceDumpInputsTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceDumpInputsTest.java index 963c2e7..cbb5100 100644 --- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceDumpInputsTest.java +++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferenceDumpInputsTest.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.tracereferences; +import static com.android.tools.r8.dump.DumpOptions.SYSTEM_PROPERTY_PREFIX; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -12,6 +13,7 @@ import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.dump.DumpOptions; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.DescriptorUtils; import com.android.tools.r8.utils.DumpInputFlags; @@ -21,25 +23,27 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.stream.Collectors; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public class TraceReferenceDumpInputsTest extends TestBase { - private final TestParameters parameters; - - @Parameterized.Parameters(name = "{0}") + @Parameters(name = "{0}") public static TestParametersCollection data() { - return getTestParameters().withSystemRuntime().build(); + return getTestParameters().withNoneRuntime().build(); } public TraceReferenceDumpInputsTest(TestParameters parameters) { - this.parameters = parameters; + parameters.assertNoneRuntime(); } @Test @@ -92,12 +96,20 @@ private void checkProperties(Path properties) throws IOException { List<String> lines = Files.readAllLines(properties); - assertEquals(4, lines.size()); + Map<String, String> systemProperties = DumpOptions.Builder.getCurrentSystemProperties(); + assertEquals(4 + systemProperties.size(), lines.size()); assertEquals("tool=TraceReferences", lines.get(0)); assertEquals( "trace_references_consumer=com.android.tools.r8.tracereferences.TraceReferencesKeepRules", lines.get(2)); assertEquals("minification=true", lines.get(3)); + Iterator<Entry<String, String>> systemPropertiesIterator = + systemProperties.entrySet().iterator(); + for (int i = 4; i < lines.size(); i++) { + assertTrue(systemPropertiesIterator.hasNext()); + Entry<String, String> entry = systemPropertiesIterator.next(); + assertEquals(SYSTEM_PROPERTY_PREFIX + entry.getKey() + "=" + entry.getValue(), lines.get(i)); + } } private void contains(Path unzipped, String jar, Class<?> clazz) throws IOException {
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java index f9f8f13..0ee68d2 100644 --- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java +++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -39,6 +39,7 @@ import java.util.Objects; import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Predicate; import java.util.stream.Collectors; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassReader; @@ -610,11 +611,15 @@ } static MethodPredicate onName(String name) { - return (access, otherName, descriptor, signature, exceptions) -> name.equals(otherName); + return onName(name::equals); + } + + static MethodPredicate onName(Predicate<String> predicate) { + return (access, otherName, descriptor, signature, exceptions) -> predicate.test(otherName); } static MethodPredicate onNames(Collection<String> names) { - return (access, otherName, descriptor, signature, exceptions) -> names.contains(otherName); + return onName(names::contains); } static MethodPredicate onNames(String... names) { @@ -758,13 +763,19 @@ } public ClassFileTransformer renameMethod(MethodPredicate predicate, String newName) { + return renameMethod(predicate, name -> newName); + } + + public ClassFileTransformer renameMethod( + MethodPredicate predicate, Function<String, String> newName) { return addClassTransformer( new ClassTransformer() { @Override public MethodVisitor visitMethod( int access, String name, String descriptor, String signature, String[] exceptions) { if (predicate.test(access, name, descriptor, signature, exceptions)) { - return super.visitMethod(access, newName, descriptor, signature, exceptions); + return super.visitMethod( + access, newName.apply(name), descriptor, signature, exceptions); } return super.visitMethod(access, name, descriptor, signature, exceptions); }
diff --git a/tools/archive_smali.py b/tools/archive_smali.py index ca503dc..db7cb89 100755 --- a/tools/archive_smali.py +++ b/tools/archive_smali.py
@@ -25,12 +25,8 @@ subprocess.check_call(['git', 'clone', REPO, temp]) return temp - def parse_options(): result = argparse.ArgumentParser(description='Release Smali') - result.add_argument('--archive-hash', - metavar=('<main hash>'), - help='The hash to use for archiving a smali build') result.add_argument('--version', metavar=('<version>'), help='The version of smali to archive.') @@ -57,9 +53,8 @@ + 'Use --dry-run to test locally') if options.checkout and not options.dry_run: raise Exception('Using local checkout is only allowed with --dry-run') - if not options.checkout: - if not options.archive_hash or not options.version: - raise Exception('Both --archive-hash and --version are required') + if not options.checkout and not options.version: + raise Exception('Option --version is required (when not using local checkout)') if utils.is_bot() and not utils.IsWindows(): set_rlimit_to_max() @@ -74,8 +69,12 @@ checkout_dir = options.checkout if options.checkout else checkout(temp) with utils.ChangedWorkingDirectory(checkout_dir): - if options.archive_hash: - subprocess.check_call(['git', 'checkout', options.archive_hash]) + if options.version: + output = subprocess.check_output(['git', 'tag', '-l', options.version]) + if len(output) == 0: + raise Exception( + 'Repository does not have a release tag for version %s' % options.version) + subprocess.check_call(['git', 'checkout', options.version]) # Find version from `build.gradle`. for line in open(os.path.join('build.gradle'), 'r'): @@ -89,7 +88,8 @@ if (options.checkout): raise Exception('Checkout %s has %s' % (options.checkout, message)) else: - raise Exception('Commit % has %s' % (options.archive_hash, message)) + raise Exception('Tag % has %s' % (options.version, message)) + print('Building version: %s' % version) # Build release to local Maven repository.
diff --git a/tools/compiledump.py b/tools/compiledump.py index 691bdeb..52805f9 100755 --- a/tools/compiledump.py +++ b/tools/compiledump.py
@@ -289,11 +289,8 @@ return dump.program_jar() def determine_class_file(args, build_properties): - if args.classfile: - return args.classfile - if 'classfile' in build_properties: - return True - return None + return args.classfile \ + or build_properties.get('backend', 'dex').lower() == 'cf' def determine_android_platform_build(args, build_properties): if args.android_platform_build:
diff --git a/tools/r8_release.py b/tools/r8_release.py index f98e24f..c799d90 100755 --- a/tools/r8_release.py +++ b/tools/r8_release.py
@@ -325,17 +325,6 @@ sources.write(re.sub(pattern, replace, line)) -def replace_startswith(prefix, replacement, path): - with open(path, "r") as source: - lines = source.readlines() - with open(path, "w") as source: - for line in lines: - if line.startswith(prefix): - source.write(replacement) - else: - source.write(line) - - def download_file(version, file, dst): dir = 'raw' if len(version) != 40 else 'raw/main' urllib.request.urlretrieve( @@ -860,12 +849,6 @@ "R8_DEV_BRANCH = '%s.%s" % (str(semver.major), str(semver.minor)), THIS_FILE_RELATIVE) - # Update main version file with the new dev branch. - replace_startswith( - ' public static final String ACTIVE_DEV_VERSION = ', - ' public static final String ACTIVE_DEV_VERSION = "' + branch_version + '.0"', - R8_VERSION_FILE) - message = \ 'Prepare %s for branch %s' % (THIS_FILE_RELATIVE, branch_version) subprocess.check_call(['git', 'commit', '-a', '-m', message])
diff --git a/tools/test.py b/tools/test.py index e7e2b99..9cfaa8b 100755 --- a/tools/test.py +++ b/tools/test.py
@@ -78,10 +78,6 @@ result.add_option('--all-tests', '--all_tests', help='Run tests in all configurations.', default=False, action='store_true') - result.add_option('--art-profile-rewriting-completeness-check', - '--art_profile_rewriting_completeness_check', - help='Enable completeness check for ART profile rewriting.', - default=False, action='store_true') result.add_option('--slow-tests', '--slow_tests', help='Also run slow tests.', default=False, action='store_true') @@ -291,8 +287,6 @@ gradle_args.append('-Ponly_internal') if options.all_tests: gradle_args.append('-Pall_tests') - if options.art_profile_rewriting_completeness_check: - gradle_args.append('-Part_profile_rewriting_completeness_check=1') if options.slow_tests: gradle_args.append('-Pslow_tests=1') if options.tool: @@ -364,6 +358,9 @@ if options.testing_state_name: gradle_args.append('-Ptesting-state-name=' + options.testing_state_name) + # Enable completeness testing of ART profile rewriting. + gradle_args.append('-Part_profile_rewriting_completeness_check=true') + # Build an R8 with dependencies for bootstrapping tests before adding test sources. gradle_args.append('r8WithRelocatedDeps') gradle_args.append('r8WithRelocatedDeps17')