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..1578174 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')