Merge commit '50f70c15b80b489373b7d6b9134062b546eb0b30' into dev-release
diff --git a/build.gradle b/build.gradle
index eca19a7..f775822 100644
--- a/build.gradle
+++ b/build.gradle
@@ -198,6 +198,11 @@
}
output.resourcesDir = 'build/classes/kotlinR8TestResources'
}
+ keepanno {
+ java {
+ srcDirs = ['src/keepanno/java']
+ }
+ }
}
// Ensure importing into IntelliJ IDEA use the same output directories as Gradle. In tests we
@@ -207,7 +212,7 @@
idea {
sourceSets.all { SourceSet sources ->
module {
- if (sources.name == "main") {
+ if (sources.name == "main" || sources.name == "keepanno") {
sourceDirs += sources.java.srcDirs
outputDir sources.output.classesDirs[0]
} else {
@@ -311,6 +316,11 @@
apiUsageSampleCompile "com.google.guava:guava:$guavaVersion"
kotlinR8TestResourcesCompileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
errorprone("com.google.errorprone:error_prone_core:$errorproneVersion")
+
+ keepannoCompile group: 'org.ow2.asm', name: 'asm', version: asmVersion
+ keepannoCompile "com.google.guava:guava:$guavaVersion"
+ testCompile sourceSets.keepanno.output
+ testRuntime sourceSets.keepanno.output
}
def r8LibPath = "$buildDir/libs/r8lib.jar"
@@ -1093,6 +1103,7 @@
task testJarSources(type: Jar, dependsOn: [testClasses, buildLibraryDesugarConversions]) {
archiveFileName = "r8testsbase.jar"
from sourceSets.test.output
+ from sourceSets.keepanno.output
// We only want to include tests that use R8 when generating keep rules for applymapping.
include "com/android/tools/r8/**"
include "android/**"
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java
new file mode 100644
index 0000000..9d0265e
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java
@@ -0,0 +1,13 @@
+// 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.keepanno.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.ANNOTATION_TYPE)
+@Retention(RetentionPolicy.CLASS)
+public @interface KeepCondition {}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstants.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstants.java
new file mode 100644
index 0000000..24e49d1
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstants.java
@@ -0,0 +1,35 @@
+// 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.keepanno.annotations;
+
+/**
+ * Utility class for referencing the various keep annotations and their structure.
+ *
+ * <p>Use of these references avoids poluting the Java namespace with imports of the java
+ * annotations which overlap in name with the actual semantic AST types.
+ */
+public final class KeepConstants {
+
+ public static String getDescriptor(Class<?> clazz) {
+ return "L" + clazz.getTypeName().replace('.', '/') + ";";
+ }
+
+ public static String getBinaryNameFromClassTypeName(String classTypeName) {
+ return classTypeName.replace('.', '/');
+ }
+
+ public static final class Edge {
+ public static final Class<KeepEdge> CLASS = KeepEdge.class;
+ public static final String DESCRIPTOR = getDescriptor(CLASS);
+ public static final String preconditions = "preconditions";
+ public static final String consequences = "consequences";
+ }
+
+ public static final class Target {
+ public static final Class<KeepTarget> CLASS = KeepTarget.class;
+ public static final String DESCRIPTOR = getDescriptor(CLASS);
+ public static final String classConstant = "classConstant";
+ public static final String methodName = "methodName";
+ }
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepEdge.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepEdge.java
new file mode 100644
index 0000000..19b1a4a
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepEdge.java
@@ -0,0 +1,17 @@
+// 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.keepanno.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.CLASS)
+public @interface KeepEdge {
+ KeepCondition[] preconditions() default {};
+
+ KeepTarget[] consequences();
+}
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
new file mode 100644
index 0000000..e20f288
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java
@@ -0,0 +1,19 @@
+// 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.keepanno.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.ANNOTATION_TYPE)
+@Retention(RetentionPolicy.CLASS)
+public @interface KeepTarget {
+ Class<?> classConstant() default Object.class;
+
+ String classTypeName() default "";
+
+ String methodName() default "";
+}
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
new file mode 100644
index 0000000..3ba3b8b
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
@@ -0,0 +1,181 @@
+// 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.keepanno.asm;
+
+import com.android.tools.r8.keepanno.annotations.KeepConstants.Edge;
+import com.android.tools.r8.keepanno.annotations.KeepConstants.Target;
+import com.android.tools.r8.keepanno.ast.KeepConsequences;
+import com.android.tools.r8.keepanno.ast.KeepEdge;
+import com.android.tools.r8.keepanno.ast.KeepEdgeException;
+import com.android.tools.r8.keepanno.ast.KeepItemPattern;
+import com.android.tools.r8.keepanno.ast.KeepItemPattern.Builder;
+import com.android.tools.r8.keepanno.ast.KeepMembersPattern;
+import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern;
+import com.android.tools.r8.keepanno.ast.KeepMethodPattern;
+import com.android.tools.r8.keepanno.ast.KeepPreconditions;
+import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern;
+import com.android.tools.r8.keepanno.ast.KeepTarget;
+import java.util.HashSet;
+import java.util.Set;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+public class KeepEdgeReader implements Opcodes {
+
+ public static int ASM_VERSION = ASM9;
+
+ public static Set<KeepEdge> readKeepEdges(byte[] classFileBytes) {
+ ClassReader reader = new ClassReader(classFileBytes);
+ Set<KeepEdge> edges = new HashSet<>();
+ reader.accept(new KeepEdgeClassVisitor(edges::add), ClassReader.SKIP_CODE);
+ return edges;
+ }
+
+ private static class KeepEdgeClassVisitor extends ClassVisitor {
+ private final Parent<KeepEdge> parent;
+
+ KeepEdgeClassVisitor(Parent<KeepEdge> parent) {
+ super(ASM_VERSION);
+ this.parent = parent;
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+ // Skip any visible annotations as @KeepEdge is not runtime visible.
+ if (!visible && descriptor.equals(Edge.DESCRIPTOR)) {
+ return new KeepEdgeVisitor(parent);
+ }
+ return null;
+ }
+ }
+
+ // Interface for providing AST result(s) for a sub-tree back up to its parent.
+ private interface Parent<T> {
+ void accept(T result);
+ }
+
+ private abstract static class AnnotationVisitorBase extends AnnotationVisitor {
+
+ AnnotationVisitorBase() {
+ super(ASM_VERSION);
+ }
+
+ @Override
+ public void visit(String name, Object value) {
+ throw new KeepEdgeException("Unexpected value in @KeepEdge: " + name + " = " + value);
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String name, String descriptor) {
+ throw new KeepEdgeException("Unexpected annotation in @KeepEdge: " + name);
+ }
+
+ @Override
+ public void visitEnum(String name, String descriptor, String value) {
+ throw new KeepEdgeException("Unexpected enum in @KeepEdge: " + name);
+ }
+
+ @Override
+ public AnnotationVisitor visitArray(String name) {
+ throw new KeepEdgeException("Unexpected array in @KeepEdge: " + name);
+ }
+ }
+
+ private static class KeepEdgeVisitor extends AnnotationVisitorBase {
+ private final Parent<KeepEdge> parent;
+ private final KeepEdge.Builder builder = KeepEdge.builder();
+
+ KeepEdgeVisitor(Parent<KeepEdge> parent) {
+ this.parent = parent;
+ }
+
+ @Override
+ public AnnotationVisitor visitArray(String name) {
+ if (name.equals(Edge.preconditions)) {
+ return new KeepPreconditionsVisitor(builder::setPreconditions);
+ }
+ if (name.equals(Edge.consequences)) {
+ return new KeepConsequencesVisitor(builder::setConsequences);
+ }
+ return super.visitArray(name);
+ }
+
+ @Override
+ public void visitEnd() {
+ parent.accept(builder.build());
+ }
+ }
+
+ private static class KeepPreconditionsVisitor extends AnnotationVisitorBase {
+ private final Parent<KeepPreconditions> parent;
+
+ public KeepPreconditionsVisitor(Parent<KeepPreconditions> parent) {
+ this.parent = parent;
+ }
+ }
+
+ private static class KeepConsequencesVisitor extends AnnotationVisitorBase {
+ private final Parent<KeepConsequences> parent;
+ private final KeepConsequences.Builder builder = KeepConsequences.builder();
+
+ public KeepConsequencesVisitor(Parent<KeepConsequences> parent) {
+ this.parent = parent;
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String name, String descriptor) {
+ if (descriptor.equals(Target.DESCRIPTOR)) {
+ return new KeepTargetVisitor(builder::addTarget);
+ }
+ return super.visitAnnotation(name, descriptor);
+ }
+
+ @Override
+ public void visitEnd() {
+ parent.accept(builder.build());
+ }
+ }
+
+ private static class KeepTargetVisitor extends AnnotationVisitorBase {
+ private final Parent<KeepTarget> parent;
+ private KeepQualifiedClassNamePattern classNamePattern = null;
+ private KeepMethodNamePattern methodName = null;
+
+ public KeepTargetVisitor(Parent<KeepTarget> parent) {
+ this.parent = parent;
+ }
+
+ @Override
+ public void visit(String name, Object value) {
+ if (name.equals(Target.classConstant) && value instanceof Type) {
+ classNamePattern = KeepQualifiedClassNamePattern.exact(((Type) value).getClassName());
+ return;
+ }
+ if (name.equals(Target.methodName) && value instanceof String) {
+ methodName = KeepMethodNamePattern.exact((String) value);
+ return;
+ }
+ super.visit(name, value);
+ }
+
+ @Override
+ public void visitEnd() {
+ Builder itemBuilder = KeepItemPattern.builder();
+ if (classNamePattern != null) {
+ itemBuilder.setClassPattern(classNamePattern);
+ }
+ if (methodName != null) {
+ itemBuilder.setMembersPattern(
+ KeepMembersPattern.builder()
+ .addMethodPattern(KeepMethodPattern.builder().setNamePattern(methodName).build())
+ .build());
+ }
+ KeepTarget target = KeepTarget.builder().setItem(itemBuilder.build()).build();
+ parent.accept(target);
+ }
+ }
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
new file mode 100644
index 0000000..1641d8d
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
@@ -0,0 +1,112 @@
+// 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.keepanno.asm;
+
+import com.android.tools.r8.keepanno.annotations.KeepConstants;
+import com.android.tools.r8.keepanno.annotations.KeepConstants.Edge;
+import com.android.tools.r8.keepanno.annotations.KeepConstants.Target;
+import com.android.tools.r8.keepanno.ast.KeepConsequences;
+import com.android.tools.r8.keepanno.ast.KeepEdge;
+import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern;
+import com.android.tools.r8.keepanno.ast.KeepPreconditions;
+import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern;
+import com.android.tools.r8.keepanno.utils.Unimplemented;
+import java.util.function.BiFunction;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+public class KeepEdgeWriter implements Opcodes {
+
+ public static void writeEdge(KeepEdge edge, ClassVisitor visitor) {
+ writeEdge(edge, visitor::visitAnnotation);
+ }
+
+ public static void writeEdge(
+ KeepEdge edge, BiFunction<String, Boolean, AnnotationVisitor> getVisitor) {
+ new KeepEdgeWriter().writeEdge(edge, getVisitor.apply(Edge.DESCRIPTOR, false));
+ }
+
+ private void writeEdge(KeepEdge edge, AnnotationVisitor visitor) {
+ writePreconditions(visitor, edge.getPreconditions());
+ writeConsequences(visitor, edge.getConsequences());
+ visitor.visitEnd();
+ }
+
+ private void writePreconditions(AnnotationVisitor visitor, KeepPreconditions preconditions) {
+ if (preconditions.isAlways()) {
+ return;
+ }
+ throw new Unimplemented();
+ }
+
+ private void writeConsequences(AnnotationVisitor visitor, KeepConsequences consequences) {
+ assert !consequences.isEmpty();
+ String ignoredArrayValueName = null;
+ AnnotationVisitor arrayVisitor = visitor.visitArray(KeepConstants.Edge.consequences);
+ consequences.forEachTarget(
+ target -> {
+ AnnotationVisitor targetVisitor =
+ arrayVisitor.visitAnnotation(ignoredArrayValueName, KeepConstants.Target.DESCRIPTOR);
+ // No options imply keep all.
+ if (!target.getOptions().isKeepAll()) {
+ throw new Unimplemented();
+ }
+ target
+ .getItem()
+ .match(
+ () -> {
+ throw new Unimplemented();
+ },
+ clazz -> {
+ KeepQualifiedClassNamePattern namePattern = clazz.getClassNamePattern();
+ if (namePattern.isExact()) {
+ Type typeConstant = Type.getType(namePattern.getExactDescriptor());
+ targetVisitor.visit(KeepConstants.Target.classConstant, typeConstant);
+ } else {
+ throw new Unimplemented();
+ }
+ if (!clazz.getExtendsPattern().isAny()) {
+ throw new Unimplemented();
+ }
+ if (clazz.getMembersPattern().isNone()) {
+ // Default is "no methods".
+ } else if (clazz.getMembersPattern().isAll()) {
+ throw new Unimplemented();
+ } else {
+ clazz
+ .getMembersPattern()
+ .forEach(
+ field -> {
+ throw new Unimplemented();
+ },
+ method -> {
+ KeepMethodNamePattern methodNamePattern = method.getNamePattern();
+ methodNamePattern.match(
+ () -> {
+ throw new Unimplemented();
+ },
+ exactMethodName -> {
+ targetVisitor.visit(Target.methodName, exactMethodName);
+ return null;
+ });
+ if (!method.getAccessPattern().isAny()) {
+ throw new Unimplemented();
+ }
+ if (!method.getReturnTypePattern().isAny()) {
+ throw new Unimplemented();
+ }
+ if (!method.getParametersPattern().isAny()) {
+ throw new Unimplemented();
+ }
+ });
+ }
+ return null;
+ });
+ targetVisitor.visitEnd();
+ });
+ arrayVisitor.visitEnd();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepCondition.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCondition.java
similarity index 95%
rename from src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepCondition.java
rename to src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCondition.java
index acd327d..43b4176 100644
--- a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepCondition.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCondition.java
@@ -1,7 +1,7 @@
// 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.experimental.keepanno.ast;
+package com.android.tools.r8.keepanno.ast;
/**
* A keep condition is the content of an item in the set of preconditions.
@@ -41,5 +41,5 @@
public KeepItemPattern getItemPattern() {
return itemPattern;
- }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepConsequences.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepConsequences.java
similarity index 64%
rename from src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepConsequences.java
rename to src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepConsequences.java
index 6a227fd..1322746 100644
--- a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepConsequences.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepConsequences.java
@@ -1,11 +1,12 @@
// 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.experimental.keepanno.ast;
+package com.android.tools.r8.keepanno.ast;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
/**
* Set of consequences of a keep edge.
@@ -29,6 +30,9 @@
}
public KeepConsequences build() {
+ if (targets.isEmpty()) {
+ throw new KeepEdgeException("Invalid empty consequent set");
+ }
return new KeepConsequences(targets);
}
}
@@ -40,6 +44,8 @@
private final List<KeepTarget> targets;
private KeepConsequences(List<KeepTarget> targets) {
+ assert targets != null;
+ assert !targets.isEmpty();
this.targets = targets;
}
@@ -50,4 +56,26 @@
public void forEachTarget(Consumer<KeepTarget> fn) {
targets.forEach(fn);
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ KeepConsequences that = (KeepConsequences) o;
+ return targets.equals(that.targets);
+ }
+
+ @Override
+ public int hashCode() {
+ return targets.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return targets.stream().map(Object::toString).collect(Collectors.joining(", "));
+ }
}
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
new file mode 100644
index 0000000..4da44aa
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
@@ -0,0 +1,125 @@
+// 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.keepanno.ast;
+
+import java.util.Objects;
+
+/**
+ * An edge in the keep graph.
+ *
+ * <p>An edge describes a set of preconditions and a set of consequences. If the preconditions are
+ * met, then the consequences are put into effect.
+ *
+ * <p>Below is a BNF of the keep edge AST for reference. The non-terminals are written in ALL_CAPS,
+ * possibly-empty repeatable subexpressions denoted with SUB* and non-empty with SUB+
+ *
+ * <p>In the Java AST, the non-terminals are prefixed with 'Keep' and in CamelCase.
+ *
+ * <p>TODO(b/248408342): Update the BNF and AST to be complete.
+ *
+ * <pre>
+ * EDGE ::= PRECONDITIONS -> CONSEQUENCES
+ *
+ * PRECONDITIONS ::= always | CONDITION+
+ * CONDITION ::= ITEM_PATTERN
+ *
+ * CONSEQUENCES ::= TARGET+
+ * TARGET ::= any | OPTIONS ITEM_PATTERN
+ * OPTIONS ::= keep-all | OPTION+
+ * OPTION ::= shrinking | optimizing | obfuscating | access-modifying
+ *
+ * ITEM_PATTERN ::= any | CLASS_PATTERN
+ * CLASS_PATTERN ::= QUALIFIED_CLASS_NAME_PATTERN extends EXTENDS_PATTERN { MEMBERS_PATTERN }
+ *
+ * TYPE_PATTERN ::= any
+ * PACKAGE_PATTERN ::= any | exact package-name
+ * QUALIFIED_CLASS_NAME_PATTERN ::= any | PACKAGE_PATTERN | UNQUALIFIED_CLASS_NAME_PATTERN
+ * UNQUALIFIED_CLASS_NAME_PATTERN ::= any | exact simple-class-name
+ * EXTENDS_PATTERN ::= any | QUALIFIED_CLASS_NAME_PATTERN
+ *
+ * MEMBERS_PATTERN ::= none | all | METHOD_PATTERN*
+ *
+ * METHOD_PATTERN
+ * ::= METHOD_ACCESS_PATTERN
+ * METHOD_RETURN_TYPE_PATTERN
+ * METHOD_NAME_PATTERN
+ * METHOD_PARAMETERS_PATTERN
+ *
+ * METHOD_ACCESS_PATTERN ::= any
+ * METHOD_NAME_PATTERN ::= any | exact method-name
+ * METHOD_RETURN_TYPE_PATTERN ::= void | TYPE_PATTERN
+ * METHOD_PARAMETERS_PATTERN ::= any | none | TYPE_PATTERN+
+ * </pre>
+ */
+public final class KeepEdge {
+
+ public static class Builder {
+ private KeepPreconditions preconditions = KeepPreconditions.always();
+ private KeepConsequences consequences;
+
+ private Builder() {}
+
+ public Builder setPreconditions(KeepPreconditions preconditions) {
+ this.preconditions = preconditions;
+ return this;
+ }
+
+ public Builder setConsequences(KeepConsequences consequences) {
+ this.consequences = consequences;
+ return this;
+ }
+
+ public KeepEdge build() {
+ if (consequences.isEmpty()) {
+ throw new KeepEdgeException("KeepEdge must have non-empty set of consequences.");
+ }
+ return new KeepEdge(preconditions, consequences);
+ }
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ private final KeepPreconditions preconditions;
+ private final KeepConsequences consequences;
+
+ private KeepEdge(KeepPreconditions preconditions, KeepConsequences consequences) {
+ assert preconditions != null;
+ assert consequences != null;
+ this.preconditions = preconditions;
+ this.consequences = consequences;
+ }
+
+ public KeepPreconditions getPreconditions() {
+ return preconditions;
+ }
+
+ public KeepConsequences getConsequences() {
+ return consequences;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ KeepEdge keepEdge = (KeepEdge) o;
+ return preconditions.equals(keepEdge.preconditions)
+ && consequences.equals(keepEdge.consequences);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(preconditions, consequences);
+ }
+
+ @Override
+ public String toString() {
+ return "KeepEdge{" + "preconditions=" + preconditions + ", consequences=" + consequences + '}';
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepEdgeException.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdgeException.java
similarity index 85%
rename from src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepEdgeException.java
rename to src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdgeException.java
index 3345c8f..5abe947 100644
--- a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepEdgeException.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdgeException.java
@@ -1,7 +1,7 @@
// 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.experimental.keepanno.ast;
+package com.android.tools.r8.keepanno.ast;
public class KeepEdgeException extends RuntimeException {
diff --git a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepExtendsPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepExtendsPattern.java
similarity index 67%
rename from src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepExtendsPattern.java
rename to src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepExtendsPattern.java
index 17e543f..b6b0fd7 100644
--- a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepExtendsPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepExtendsPattern.java
@@ -1,7 +1,7 @@
// 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.experimental.keepanno.ast;
+package com.android.tools.r8.keepanno.ast;
/** Pattern for matching the "extends" or "implements" clause of a class. */
public abstract class KeepExtendsPattern {
@@ -42,6 +42,21 @@
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() {
+ return "*";
+ }
}
private static class KeepExtendsClassPattern extends KeepExtendsPattern {
@@ -49,6 +64,7 @@
private final KeepQualifiedClassNamePattern pattern;
public KeepExtendsClassPattern(KeepQualifiedClassNamePattern pattern) {
+ assert pattern != null;
this.pattern = pattern;
}
@@ -56,6 +72,28 @@
public boolean isAny() {
return pattern.isAny();
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ KeepExtendsClassPattern that = (KeepExtendsClassPattern) o;
+ return pattern.equals(that.pattern);
+ }
+
+ @Override
+ public int hashCode() {
+ return pattern.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return pattern.toString();
+ }
}
public static Builder builder() {
diff --git a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepFieldPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldPattern.java
similarity index 86%
rename from src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepFieldPattern.java
rename to src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldPattern.java
index 04e2697..8de46e1 100644
--- a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepFieldPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldPattern.java
@@ -1,7 +1,7 @@
// 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.experimental.keepanno.ast;
+package com.android.tools.r8.keepanno.ast;
public class KeepFieldPattern extends KeepMemberPattern {
diff --git a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepItemPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepItemPattern.java
similarity index 75%
rename from src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepItemPattern.java
rename to src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepItemPattern.java
index 3c6f947..e5b0987 100644
--- a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepItemPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepItemPattern.java
@@ -1,8 +1,9 @@
// 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.experimental.keepanno.ast;
+package com.android.tools.r8.keepanno.ast;
+import java.util.Objects;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -87,6 +88,21 @@
public <T> T match(Supplier<T> onAny, Function<KeepClassPattern, T> onItem) {
return onAny.get();
}
+
+ @Override
+ public boolean equals(Object obj) {
+ return this == obj;
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(this);
+ }
+
+ @Override
+ public String toString() {
+ return "*";
+ }
}
public static class KeepClassPattern extends KeepItemPattern {
@@ -100,6 +116,9 @@
KeepQualifiedClassNamePattern qualifiedClassPattern,
KeepExtendsPattern extendsPattern,
KeepMembersPattern membersPattern) {
+ assert qualifiedClassPattern != null;
+ assert extendsPattern != null;
+ assert membersPattern != null;
this.qualifiedClassPattern = qualifiedClassPattern;
this.extendsPattern = extendsPattern;
this.membersPattern = membersPattern;
@@ -130,6 +149,37 @@
public KeepMembersPattern getMembersPattern() {
return membersPattern;
}
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ KeepClassPattern that = (KeepClassPattern) obj;
+ return qualifiedClassPattern.equals(that.qualifiedClassPattern)
+ && extendsPattern.equals(that.extendsPattern)
+ && membersPattern.equals(that.membersPattern);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(qualifiedClassPattern, extendsPattern, membersPattern);
+ }
+
+ @Override
+ public String toString() {
+ return "KeepClassPattern{"
+ + "qualifiedClassPattern="
+ + qualifiedClassPattern
+ + ", extendsPattern="
+ + extendsPattern
+ + ", membersPattern="
+ + membersPattern
+ + '}';
+ }
}
public abstract boolean isAny();
diff --git a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMemberPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberPattern.java
similarity index 93%
rename from src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMemberPattern.java
rename to src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberPattern.java
index 73bae7e..bd716e9 100644
--- a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMemberPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMemberPattern.java
@@ -1,7 +1,7 @@
// 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.experimental.keepanno.ast;
+package com.android.tools.r8.keepanno.ast;
public abstract class KeepMemberPattern {
diff --git a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMembersPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMembersPattern.java
similarity index 73%
rename from src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMembersPattern.java
rename to src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMembersPattern.java
index 6d3f93e..be5af32 100644
--- a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMembersPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMembersPattern.java
@@ -1,12 +1,14 @@
// 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.experimental.keepanno.ast;
+package com.android.tools.r8.keepanno.ast;
-import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.keepanno.utils.Unimplemented;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
public abstract class KeepMembersPattern {
@@ -89,6 +91,21 @@
public void forEach(Consumer<KeepFieldPattern> onField, Consumer<KeepMethodPattern> onMethod) {
throw new Unimplemented("Should this include all and none?");
}
+
+ @Override
+ public boolean equals(Object obj) {
+ return this == obj;
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(this);
+ }
+
+ @Override
+ public String toString() {
+ return "*";
+ }
}
private static class KeepMembersNonePattern extends KeepMembersPattern {
@@ -116,6 +133,21 @@
public void forEach(Consumer<KeepFieldPattern> onField, Consumer<KeepMethodPattern> onMethod) {
throw new Unimplemented("Should this include all and none?");
}
+
+ @Override
+ public boolean equals(Object obj) {
+ return this == obj;
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(this);
+ }
+
+ @Override
+ public String toString() {
+ return "<none>";
+ }
}
private static class KeepMembersSomePattern extends KeepMembersPattern {
@@ -146,6 +178,33 @@
fields.forEach(onField);
methods.forEach(onMethod);
}
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ KeepMembersSomePattern that = (KeepMembersSomePattern) obj;
+ return methods.equals(that.methods) && fields.equals(that.fields);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(methods, fields);
+ }
+
+ @Override
+ public String toString() {
+ return "KeepMembersSomePattern{"
+ + "methods={"
+ + methods.stream().map(Object::toString).collect(Collectors.joining(", "))
+ + "}, fields={"
+ + fields.stream().map(Object::toString).collect(Collectors.joining(", "))
+ + "}}";
+ }
}
private KeepMembersPattern() {}
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
new file mode 100644
index 0000000..fd0fd3e
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodAccessPattern.java
@@ -0,0 +1,46 @@
+// 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.keepanno.ast;
+
+// TODO: finish this.
+public abstract class KeepMethodAccessPattern {
+
+ public static KeepMethodAccessPattern any() {
+ return Any.getInstance();
+ }
+
+ public abstract boolean isAny();
+
+ private static class Any extends KeepMethodAccessPattern {
+
+ private static Any INSTANCE = null;
+
+ private static Any getInstance() {
+ if (INSTANCE == null) {
+ INSTANCE = new Any();
+ }
+ 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() {
+ return "*";
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodNamePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodNamePattern.java
similarity index 68%
rename from src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodNamePattern.java
rename to src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodNamePattern.java
index ada6701..6dc373d 100644
--- a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodNamePattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodNamePattern.java
@@ -1,7 +1,7 @@
// 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.experimental.keepanno.ast;
+package com.android.tools.r8.keepanno.ast;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -47,12 +47,28 @@
public <T> T match(Supplier<T> onAny, Function<String, T> onExact) {
return onAny.get();
}
+
+ @Override
+ public boolean equals(Object obj) {
+ return this == obj;
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(this);
+ }
+
+ @Override
+ public String toString() {
+ return "*";
+ }
}
private static class KeepMethodNameExactPattern extends KeepMethodNamePattern {
private final String name;
public KeepMethodNameExactPattern(String name) {
+ assert name != null;
this.name = name;
}
@@ -60,5 +76,27 @@
public <T> T match(Supplier<T> onAny, Function<String, T> onExact) {
return onExact.apply(name);
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ KeepMethodNameExactPattern that = (KeepMethodNameExactPattern) o;
+ return name.equals(that.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodParametersPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodParametersPattern.java
similarity index 70%
rename from src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodParametersPattern.java
rename to src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodParametersPattern.java
index 4f42202..83b4606 100644
--- a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodParametersPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodParametersPattern.java
@@ -1,7 +1,7 @@
// 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.experimental.keepanno.ast;
+package com.android.tools.r8.keepanno.ast;
import java.util.Collections;
import java.util.List;
@@ -22,6 +22,10 @@
public abstract <T> T match(Supplier<T> onAny, Function<List<KeepTypePattern>, T> onList);
+ public boolean isAny() {
+ return match(() -> true, params -> false);
+ }
+
private static class None extends KeepMethodParametersPattern {
private static None INSTANCE = null;
@@ -36,6 +40,21 @@
public <T> T match(Supplier<T> onAny, Function<List<KeepTypePattern>, T> onList) {
return onList.apply(Collections.emptyList());
}
+
+ @Override
+ public boolean equals(Object obj) {
+ return this == obj;
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(this);
+ }
+
+ @Override
+ public String toString() {
+ return "()";
+ }
}
private static class Any extends KeepMethodParametersPattern {
@@ -52,5 +71,20 @@
public <T> T match(Supplier<T> onAny, Function<List<KeepTypePattern>, T> onList) {
return onAny.get();
}
+
+ @Override
+ public boolean equals(Object obj) {
+ return this == obj;
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(this);
+ }
+
+ @Override
+ public String toString() {
+ return "(...)";
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java
similarity index 71%
rename from src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodPattern.java
rename to src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java
index 5db7d26..b3ec418 100644
--- a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java
@@ -1,7 +1,9 @@
// 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.experimental.keepanno.ast;
+package com.android.tools.r8.keepanno.ast;
+
+import java.util.Objects;
public final class KeepMethodPattern extends KeepMemberPattern {
@@ -62,6 +64,10 @@
KeepMethodNamePattern namePattern,
KeepMethodReturnTypePattern returnTypePattern,
KeepMethodParametersPattern parametersPattern) {
+ assert accessPattern != null;
+ assert namePattern != null;
+ assert returnTypePattern != null;
+ assert parametersPattern != null;
this.accessPattern = accessPattern;
this.namePattern = namePattern;
this.returnTypePattern = returnTypePattern;
@@ -87,4 +93,38 @@
public KeepMethodParametersPattern getParametersPattern() {
return parametersPattern;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ KeepMethodPattern that = (KeepMethodPattern) o;
+ return accessPattern.equals(that.accessPattern)
+ && namePattern.equals(that.namePattern)
+ && returnTypePattern.equals(that.returnTypePattern)
+ && parametersPattern.equals(that.parametersPattern);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(accessPattern, namePattern, returnTypePattern, parametersPattern);
+ }
+
+ @Override
+ public String toString() {
+ return "KeepMethodPattern{"
+ + "access="
+ + accessPattern
+ + ", name="
+ + namePattern
+ + ", returnType="
+ + returnTypePattern
+ + ", parameters="
+ + parametersPattern
+ + '}';
+ }
}
diff --git a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodReturnTypePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodReturnTypePattern.java
similarity index 63%
rename from src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodReturnTypePattern.java
rename to src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodReturnTypePattern.java
index 4e7b5b9..f711bc0 100644
--- a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodReturnTypePattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodReturnTypePattern.java
@@ -1,7 +1,7 @@
// 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.experimental.keepanno.ast;
+package com.android.tools.r8.keepanno.ast;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -23,6 +23,10 @@
public abstract <T> T match(Supplier<T> onVoid, Function<KeepTypePattern, T> onType);
+ public boolean isAny() {
+ return match(() -> false, KeepTypePattern::isAny);
+ }
+
private static class VoidType extends KeepMethodReturnTypePattern {
private static VoidType INSTANCE = null;
@@ -37,6 +41,21 @@
public <T> T match(Supplier<T> onVoid, Function<KeepTypePattern, T> onType) {
return onVoid.get();
}
+
+ @Override
+ public boolean equals(Object obj) {
+ return this == obj;
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(this);
+ }
+
+ @Override
+ public String toString() {
+ return "void";
+ }
}
private static class SomeType extends KeepMethodReturnTypePattern {
@@ -44,6 +63,7 @@
private final KeepTypePattern typePattern;
private SomeType(KeepTypePattern typePattern) {
+ assert typePattern != null;
this.typePattern = typePattern;
}
@@ -51,5 +71,27 @@
public <T> T match(Supplier<T> onVoid, Function<KeepTypePattern, T> onType) {
return onType.apply(typePattern);
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ SomeType someType = (SomeType) o;
+ return typePattern.equals(someType.typePattern);
+ }
+
+ @Override
+ public int hashCode() {
+ return typePattern.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return typePattern.toString();
+ }
}
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepOptions.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepOptions.java
new file mode 100644
index 0000000..21b7487
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepOptions.java
@@ -0,0 +1,135 @@
+// 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.keepanno.ast;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public final class KeepOptions {
+
+ public boolean isKeepAll() {
+ return allowedOptions.isEmpty();
+ }
+
+ public enum KeepOption {
+ SHRINKING,
+ OPTIMIZING,
+ OBFUSCATING,
+ ACCESS_MODIFYING,
+ }
+
+ public static KeepOptions keepAll() {
+ if (ALLOW_NONE_INSTANCE == null) {
+ ALLOW_NONE_INSTANCE = new KeepOptions(ImmutableSet.of());
+ }
+ return ALLOW_NONE_INSTANCE;
+ }
+
+ public static Builder allowBuilder() {
+ return new Builder(true);
+ }
+
+ public static Builder disallowBuilder() {
+ return new Builder(false);
+ }
+
+ public static KeepOptions allow(KeepOption... options) {
+ return allowBuilder().addAll(options).build();
+ }
+
+ public static KeepOptions disallow(KeepOption... options) {
+ return disallowBuilder().addAll(options).build();
+ }
+
+ public static class Builder {
+ public final boolean allowIfSet;
+ public Set<KeepOption> options = new HashSet<>();
+
+ private Builder(boolean allowIfSet) {
+ this.allowIfSet = allowIfSet;
+ }
+
+ public Builder add(KeepOption option) {
+ options.add(option);
+ return this;
+ }
+
+ public Builder addAll(KeepOption... options) {
+ return addAll(Arrays.asList(options));
+ }
+
+ public Builder addAll(Collection<KeepOption> options) {
+ this.options.addAll(options);
+ return this;
+ }
+
+ public KeepOptions build() {
+ // Fast path check for the two variants of "keep all".
+ if (options.isEmpty()) {
+ if (allowIfSet) {
+ return keepAll();
+ }
+ throw new KeepEdgeException("Invalid keep options that disallow nothing.");
+ }
+ if (options.size() == KeepOption.values().length) {
+ if (!allowIfSet) {
+ return keepAll();
+ }
+ throw new KeepEdgeException("Invalid keep options that allow everything.");
+ }
+ // The normalized options is the "allow variant", if not of that form invert it on build.
+ if (allowIfSet) {
+ return new KeepOptions(ImmutableSet.copyOf(options));
+ }
+ ImmutableSet.Builder<KeepOption> invertedOptions = ImmutableSet.builder();
+ for (KeepOption option : KeepOption.values()) {
+ if (!options.contains(option)) {
+ invertedOptions.add(option);
+ }
+ }
+ return new KeepOptions(invertedOptions.build());
+ }
+ }
+
+ private static KeepOptions ALLOW_NONE_INSTANCE = null;
+
+ private final ImmutableSet<KeepOption> allowedOptions;
+
+ private KeepOptions(ImmutableSet<KeepOption> options) {
+ this.allowedOptions = options;
+ }
+
+ public boolean isAllowed(KeepOption option) {
+ return allowedOptions.contains(option);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ KeepOptions that = (KeepOptions) o;
+ return allowedOptions.equals(that.allowedOptions);
+ }
+
+ @Override
+ public int hashCode() {
+ return allowedOptions.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "KeepOptions{"
+ + allowedOptions.stream().map(Objects::toString).collect(Collectors.joining(", "))
+ + '}';
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepPackagePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepPackagePattern.java
similarity index 93%
rename from src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepPackagePattern.java
rename to src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepPackagePattern.java
index 10db385..7d23490 100644
--- a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepPackagePattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepPackagePattern.java
@@ -1,7 +1,7 @@
// 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.experimental.keepanno.ast;
+package com.android.tools.r8.keepanno.ast;
public abstract class KeepPackagePattern {
@@ -88,6 +88,11 @@
public int hashCode() {
return System.identityHashCode(this);
}
+
+ @Override
+ public String toString() {
+ return "*";
+ }
}
private static final class KeepPackageTopPattern extends KeepPackageExactPattern {
@@ -114,6 +119,11 @@
public boolean isTop() {
return true;
}
+
+ @Override
+ public String toString() {
+ return "";
+ }
}
public static class KeepPackageExactPattern extends KeepPackagePattern {
@@ -166,6 +176,11 @@
public int hashCode() {
return fullPackage.hashCode();
}
+
+ @Override
+ public String toString() {
+ return fullPackage;
+ }
}
public abstract boolean isAny();
diff --git a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepPreconditions.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepPreconditions.java
similarity index 69%
rename from src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepPreconditions.java
rename to src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepPreconditions.java
index f3083df..f672013 100644
--- a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepPreconditions.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepPreconditions.java
@@ -1,7 +1,7 @@
// 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.experimental.keepanno.ast;
+package com.android.tools.r8.keepanno.ast;
import java.util.ArrayList;
import java.util.List;
@@ -59,6 +59,21 @@
public void forEach(Consumer<KeepCondition> fn) {
// Empty.
}
+
+ @Override
+ public boolean equals(Object obj) {
+ return this == obj;
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(this);
+ }
+
+ @Override
+ public String toString() {
+ return "true";
+ }
}
private static class KeepPreconditionsSome extends KeepPreconditions {
@@ -66,6 +81,8 @@
private final List<KeepCondition> preconditions;
private KeepPreconditionsSome(List<KeepCondition> preconditions) {
+ assert preconditions != null;
+ assert !preconditions.isEmpty();
this.preconditions = preconditions;
}
@@ -78,5 +95,27 @@
public void forEach(Consumer<KeepCondition> fn) {
preconditions.forEach(fn);
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ KeepPreconditionsSome that = (KeepPreconditionsSome) o;
+ return preconditions.equals(that.preconditions);
+ }
+
+ @Override
+ public int hashCode() {
+ return preconditions.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return preconditions.toString();
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepQualifiedClassNamePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepQualifiedClassNamePattern.java
similarity index 82%
rename from src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepQualifiedClassNamePattern.java
rename to src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepQualifiedClassNamePattern.java
index 9485580..97d01b9 100644
--- a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepQualifiedClassNamePattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepQualifiedClassNamePattern.java
@@ -1,7 +1,7 @@
// 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.experimental.keepanno.ast;
+package com.android.tools.r8.keepanno.ast;
import java.util.Objects;
@@ -74,6 +74,21 @@
return packagePattern.isAny() && namePattern.isAny();
}
+ public boolean isExact() {
+ return packagePattern.isExact() && namePattern.isExact();
+ }
+
+ public String getExactDescriptor() {
+ if (!isExact()) {
+ throw new KeepEdgeException("Attempt to obtain exact qualified type for inexact pattern");
+ }
+ return 'L'
+ + packagePattern.asExact().getExactPackageAsString().replace('.', '/')
+ + (packagePattern.isTop() ? "" : "/")
+ + namePattern.asExact().getExactNameAsString()
+ + ';';
+ }
+
public KeepPackagePattern getPackagePattern() {
return packagePattern;
}
@@ -98,4 +113,9 @@
public int hashCode() {
return Objects.hash(packagePattern.hashCode(), namePattern.hashCode());
}
+
+ @Override
+ public String toString() {
+ return packagePattern + (packagePattern.isTop() ? "" : ".") + namePattern;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepTarget.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTarget.java
similarity index 67%
rename from src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepTarget.java
rename to src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTarget.java
index a63a34a..111022b 100644
--- a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepTarget.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTarget.java
@@ -1,7 +1,9 @@
// 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.experimental.keepanno.ast;
+package com.android.tools.r8.keepanno.ast;
+
+import java.util.Objects;
public class KeepTarget {
@@ -38,6 +40,8 @@
private final KeepOptions options;
private KeepTarget(KeepItemPattern item, KeepOptions options) {
+ assert item != null;
+ assert options != null;
this.item = item;
this.options = options;
}
@@ -53,4 +57,26 @@
public KeepOptions getOptions() {
return options;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ KeepTarget that = (KeepTarget) o;
+ return item.equals(that.item) && options.equals(that.options);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(item, options);
+ }
+
+ @Override
+ public String toString() {
+ return "KeepTarget{" + "item=" + item + ", options=" + options + '}';
+ }
}
diff --git a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepTypePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTypePattern.java
similarity index 69%
rename from src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepTypePattern.java
rename to src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTypePattern.java
index eff9e9c..a790094 100644
--- a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepTypePattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTypePattern.java
@@ -1,7 +1,7 @@
// 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.experimental.keepanno.ast;
+package com.android.tools.r8.keepanno.ast;
public abstract class KeepTypePattern {
@@ -23,6 +23,21 @@
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() {
+ return "*";
+ }
}
public abstract boolean isAny();
diff --git a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepUnqualfiedClassNamePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepUnqualfiedClassNamePattern.java
similarity index 93%
rename from src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepUnqualfiedClassNamePattern.java
rename to src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepUnqualfiedClassNamePattern.java
index ab38ed2..c9ac380 100644
--- a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepUnqualfiedClassNamePattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepUnqualfiedClassNamePattern.java
@@ -1,7 +1,7 @@
// 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.experimental.keepanno.ast;
+package com.android.tools.r8.keepanno.ast;
public abstract class KeepUnqualfiedClassNamePattern {
@@ -71,6 +71,11 @@
public int hashCode() {
return System.identityHashCode(this);
}
+
+ @Override
+ public String toString() {
+ return "*";
+ }
}
public static class KeepClassNameExactPattern extends KeepUnqualfiedClassNamePattern {
@@ -117,6 +122,11 @@
public int hashCode() {
return className.hashCode();
}
+
+ @Override
+ public String toString() {
+ return className;
+ }
}
public abstract boolean isAny();
diff --git a/src/main/java/com/android/tools/r8/experimental/keepanno/keeprules/KeepRuleExtractor.java b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
similarity index 83%
rename from src/main/java/com/android/tools/r8/experimental/keepanno/keeprules/KeepRuleExtractor.java
rename to src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
index 6c24ec1..d9b75ae 100644
--- a/src/main/java/com/android/tools/r8/experimental/keepanno/keeprules/KeepRuleExtractor.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
@@ -1,32 +1,31 @@
// 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.experimental.keepanno.keeprules;
+package com.android.tools.r8.keepanno.keeprules;
-import com.android.tools.r8.errors.Unimplemented;
-import com.android.tools.r8.experimental.keepanno.ast.KeepConsequences;
-import com.android.tools.r8.experimental.keepanno.ast.KeepEdge;
-import com.android.tools.r8.experimental.keepanno.ast.KeepFieldPattern;
-import com.android.tools.r8.experimental.keepanno.ast.KeepItemPattern.KeepClassPattern;
-import com.android.tools.r8.experimental.keepanno.ast.KeepMembersPattern;
-import com.android.tools.r8.experimental.keepanno.ast.KeepMethodAccessPattern;
-import com.android.tools.r8.experimental.keepanno.ast.KeepMethodNamePattern;
-import com.android.tools.r8.experimental.keepanno.ast.KeepMethodParametersPattern;
-import com.android.tools.r8.experimental.keepanno.ast.KeepMethodPattern;
-import com.android.tools.r8.experimental.keepanno.ast.KeepMethodReturnTypePattern;
-import com.android.tools.r8.experimental.keepanno.ast.KeepOptions;
-import com.android.tools.r8.experimental.keepanno.ast.KeepOptions.KeepOption;
-import com.android.tools.r8.experimental.keepanno.ast.KeepPackagePattern;
-import com.android.tools.r8.experimental.keepanno.ast.KeepPreconditions;
-import com.android.tools.r8.experimental.keepanno.ast.KeepQualifiedClassNamePattern;
-import com.android.tools.r8.experimental.keepanno.ast.KeepTarget;
-import com.android.tools.r8.experimental.keepanno.ast.KeepTypePattern;
-import com.android.tools.r8.experimental.keepanno.ast.KeepUnqualfiedClassNamePattern;
-import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.StringUtils.BraceType;
+import com.android.tools.r8.keepanno.ast.KeepConsequences;
+import com.android.tools.r8.keepanno.ast.KeepEdge;
+import com.android.tools.r8.keepanno.ast.KeepFieldPattern;
+import com.android.tools.r8.keepanno.ast.KeepItemPattern.KeepClassPattern;
+import com.android.tools.r8.keepanno.ast.KeepMembersPattern;
+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;
+import com.android.tools.r8.keepanno.ast.KeepMethodReturnTypePattern;
+import com.android.tools.r8.keepanno.ast.KeepOptions;
+import com.android.tools.r8.keepanno.ast.KeepOptions.KeepOption;
+import com.android.tools.r8.keepanno.ast.KeepPackagePattern;
+import com.android.tools.r8.keepanno.ast.KeepPreconditions;
+import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern;
+import com.android.tools.r8.keepanno.ast.KeepTarget;
+import com.android.tools.r8.keepanno.ast.KeepTypePattern;
+import com.android.tools.r8.keepanno.ast.KeepUnqualfiedClassNamePattern;
+import com.android.tools.r8.keepanno.utils.Unimplemented;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
public class KeepRuleExtractor {
@@ -151,7 +150,11 @@
StringBuilder builder, KeepMethodParametersPattern parametersPattern) {
return parametersPattern.match(
() -> builder.append("(***)"),
- list -> StringUtils.append(builder, list, ", ", BraceType.PARENS));
+ list ->
+ builder
+ .append('(')
+ .append(list.stream().map(Object::toString).collect(Collectors.joining(", ")))
+ .append(')'));
}
private static StringBuilder printMethodName(
@@ -220,7 +223,7 @@
return "optimization";
case OBFUSCATING:
return "obfuscation";
- case ACCESS_MODIFING:
+ case ACCESS_MODIFYING:
return "accessmodification";
default:
throw new Unimplemented();
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessor.java b/src/keepanno/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessor.java
new file mode 100644
index 0000000..36bbaeb
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessor.java
@@ -0,0 +1,279 @@
+// 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.keepanno.processor;
+
+import static org.objectweb.asm.Opcodes.ACC_FINAL;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static org.objectweb.asm.Opcodes.ACC_SUPER;
+
+import com.android.tools.r8.keepanno.annotations.KeepConstants;
+import com.android.tools.r8.keepanno.annotations.KeepConstants.Edge;
+import com.android.tools.r8.keepanno.annotations.KeepConstants.Target;
+import com.android.tools.r8.keepanno.asm.KeepEdgeReader;
+import com.android.tools.r8.keepanno.asm.KeepEdgeWriter;
+import com.android.tools.r8.keepanno.ast.KeepConsequences;
+import com.android.tools.r8.keepanno.ast.KeepEdge;
+import com.android.tools.r8.keepanno.ast.KeepEdge.Builder;
+import com.android.tools.r8.keepanno.ast.KeepItemPattern;
+import com.android.tools.r8.keepanno.ast.KeepMembersPattern;
+import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern;
+import com.android.tools.r8.keepanno.ast.KeepMethodPattern;
+import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern;
+import com.android.tools.r8.keepanno.ast.KeepTarget;
+import com.android.tools.r8.keepanno.utils.Unimplemented;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.annotation.processing.SupportedSourceVersion;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.SimpleAnnotationValueVisitor7;
+import javax.lang.model.util.SimpleTypeVisitor7;
+import javax.tools.Diagnostic.Kind;
+import javax.tools.JavaFileObject;
+import org.objectweb.asm.ClassWriter;
+
+@SupportedAnnotationTypes("com.android.tools.r8.keepanno.annotations.*")
+@SupportedSourceVersion(SourceVersion.RELEASE_7)
+public class KeepEdgeProcessor extends AbstractProcessor {
+
+ public static String getClassTypeNameForSynthesizedEdges(String classTypeName) {
+ return classTypeName + "$$KeepEdges";
+ }
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ for (Element rootElement : roundEnv.getRootElements()) {
+ TypeElement typeElement = getEnclosingTypeElement(rootElement);
+ KeepEdge edge = processKeepEdge(typeElement, roundEnv);
+ if (edge != null) {
+ String edgeTargetClass =
+ getClassTypeNameForSynthesizedEdges(typeElement.getQualifiedName().toString());
+ byte[] writtenEdge = writeEdge(edge, edgeTargetClass);
+ Filer filer = processingEnv.getFiler();
+ try {
+ JavaFileObject classFile = filer.createClassFile(edgeTargetClass);
+ classFile.openOutputStream().write(writtenEdge);
+ } catch (IOException e) {
+ error(e.getMessage());
+ }
+ }
+ }
+ return true;
+ }
+
+ private static byte[] writeEdge(KeepEdge edge, String classTypeName) {
+ String classBinaryName = KeepConstants.getBinaryNameFromClassTypeName(classTypeName);
+ ClassWriter classWriter = new ClassWriter(0);
+ classWriter.visit(
+ KeepEdgeReader.ASM_VERSION,
+ ACC_PUBLIC | ACC_FINAL | ACC_SUPER,
+ classBinaryName,
+ null,
+ "java/lang/Object",
+ null);
+ classWriter.visitSource("SynthesizedKeepEdge", null);
+ KeepEdgeWriter.writeEdge(edge, classWriter);
+ classWriter.visitEnd();
+ return classWriter.toByteArray();
+ }
+
+ private KeepEdge processKeepEdge(TypeElement keepEdge, RoundEnvironment roundEnv) {
+ AnnotationMirror mirror = getAnnotationMirror(keepEdge, KeepConstants.Edge.CLASS);
+ if (mirror == null) {
+ return null;
+ }
+ Builder edgeBuilder = KeepEdge.builder();
+ processPreconditions(edgeBuilder, mirror);
+ processConsequences(edgeBuilder, mirror);
+ return edgeBuilder.build();
+ }
+
+ private void processPreconditions(Builder edgeBuilder, AnnotationMirror mirror) {
+ AnnotationValue preconditions = getAnnotationValue(mirror, Edge.preconditions);
+ if (preconditions == null) {
+ return;
+ }
+ throw new Unimplemented();
+ }
+
+ private void processConsequences(Builder edgeBuilder, AnnotationMirror mirror) {
+ AnnotationValue consequences = getAnnotationValue(mirror, Edge.consequences);
+ if (consequences == null) {
+ return;
+ }
+ KeepConsequences.Builder consequencesBuilder = KeepConsequences.builder();
+ new AnnotationListValueVisitor(
+ value -> {
+ KeepTarget.Builder targetBuilder = KeepTarget.builder();
+ processTarget(targetBuilder, AnnotationMirrorValueVisitor.getMirror(value));
+ consequencesBuilder.addTarget(targetBuilder.build());
+ })
+ .onValue(consequences);
+ edgeBuilder.setConsequences(consequencesBuilder.build());
+ }
+
+ private void processTarget(KeepTarget.Builder builder, AnnotationMirror mirror) {
+ KeepItemPattern.Builder itemBuilder = KeepItemPattern.builder();
+ AnnotationValue classConstantValue = getAnnotationValue(mirror, Target.classConstant);
+ if (classConstantValue != null) {
+ DeclaredType type = AnnotationClassValueVisitor.getType(classConstantValue);
+ itemBuilder.setClassPattern(KeepQualifiedClassNamePattern.exact(type.toString()));
+ }
+ AnnotationValue methodNameValue = getAnnotationValue(mirror, Target.methodName);
+ if (methodNameValue != null) {
+ String methodName = AnnotationStringValueVisitor.getString(methodNameValue);
+ itemBuilder.setMembersPattern(
+ KeepMembersPattern.builder()
+ .addMethodPattern(
+ KeepMethodPattern.builder()
+ .setNamePattern(KeepMethodNamePattern.exact(methodName))
+ .build())
+ .build());
+ }
+
+ builder.setItem(itemBuilder.build());
+ }
+
+ private void error(String message) {
+ processingEnv.getMessager().printMessage(Kind.ERROR, message);
+ }
+
+ private static TypeElement getEnclosingTypeElement(Element element) {
+ while (true) {
+ if (element == null || element instanceof TypeElement) {
+ return (TypeElement) element;
+ }
+ element = element.getEnclosingElement();
+ }
+ }
+
+ private static AnnotationMirror getAnnotationMirror(TypeElement typeElement, Class<?> clazz) {
+ String clazzName = clazz.getName();
+ for (AnnotationMirror m : typeElement.getAnnotationMirrors()) {
+ if (m.getAnnotationType().toString().equals(clazzName)) {
+ return m;
+ }
+ }
+ return null;
+ }
+
+ private static AnnotationValue getAnnotationValue(AnnotationMirror annotationMirror, String key) {
+ for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry :
+ annotationMirror.getElementValues().entrySet()) {
+ if (entry.getKey().getSimpleName().toString().equals(key)) {
+ return entry.getValue();
+ }
+ }
+ return null;
+ }
+
+ /// Annotation Visitors
+
+ private abstract static class AnnotationValueVisitorBase<T>
+ extends SimpleAnnotationValueVisitor7<T, Object> {
+ @Override
+ protected T defaultAction(Object o1, Object o2) {
+ throw new IllegalStateException();
+ }
+
+ public T onValue(AnnotationValue value) {
+ return value.accept(this, null);
+ }
+ }
+
+ private static class AnnotationListValueVisitor
+ extends AnnotationValueVisitorBase<AnnotationListValueVisitor> {
+
+ private final Consumer<AnnotationValue> fn;
+
+ public AnnotationListValueVisitor(Consumer<AnnotationValue> fn) {
+ this.fn = fn;
+ }
+
+ @Override
+ public AnnotationListValueVisitor visitArray(
+ List<? extends AnnotationValue> values, Object ignore) {
+ values.forEach(fn);
+ return this;
+ }
+ }
+
+ private static class AnnotationMirrorValueVisitor
+ extends AnnotationValueVisitorBase<AnnotationMirrorValueVisitor> {
+
+ private AnnotationMirror mirror = null;
+
+ public static AnnotationMirror getMirror(AnnotationValue value) {
+ return new AnnotationMirrorValueVisitor().onValue(value).mirror;
+ }
+
+ @Override
+ public AnnotationMirrorValueVisitor visitAnnotation(AnnotationMirror mirror, Object o) {
+ this.mirror = mirror;
+ return this;
+ }
+ }
+
+ private static class AnnotationStringValueVisitor
+ extends AnnotationValueVisitorBase<AnnotationStringValueVisitor> {
+ private String string;
+
+ public static String getString(AnnotationValue value) {
+ return new AnnotationStringValueVisitor().onValue(value).string;
+ }
+
+ @Override
+ public AnnotationStringValueVisitor visitString(String string, Object ignore) {
+ this.string = string;
+ return this;
+ }
+ }
+
+ private static class AnnotationClassValueVisitor
+ extends AnnotationValueVisitorBase<AnnotationClassValueVisitor> {
+ private DeclaredType type = null;
+
+ public static DeclaredType getType(AnnotationValue value) {
+ return new AnnotationClassValueVisitor().onValue(value).type;
+ }
+
+ @Override
+ public AnnotationClassValueVisitor visitType(TypeMirror t, Object ignore) {
+ ClassTypeVisitor classTypeVisitor = new ClassTypeVisitor();
+ t.accept(classTypeVisitor, null);
+ type = classTypeVisitor.type;
+ return this;
+ }
+ }
+
+ private static class TypeVisitorBase<T> extends SimpleTypeVisitor7<T, Object> {
+ @Override
+ protected T defaultAction(TypeMirror typeMirror, Object ignore) {
+ throw new IllegalStateException();
+ }
+ }
+
+ private static class ClassTypeVisitor extends TypeVisitorBase<ClassTypeVisitor> {
+ private DeclaredType type = null;
+
+ @Override
+ public ClassTypeVisitor visitDeclared(DeclaredType t, Object ignore) {
+ this.type = t;
+ return this;
+ }
+ }
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/utils/Unimplemented.java b/src/keepanno/java/com/android/tools/r8/keepanno/utils/Unimplemented.java
new file mode 100644
index 0000000..8293126
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/utils/Unimplemented.java
@@ -0,0 +1,12 @@
+// 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.keepanno.utils;
+
+public class Unimplemented extends RuntimeException {
+ public Unimplemented() {}
+
+ public Unimplemented(String msg) {
+ super(msg);
+ }
+}
diff --git a/src/library_desugar/java/java/nio/file/FileApiFlips.java b/src/library_desugar/java/java/nio/file/FileApiFlips.java
index b68aeb2..9c8a46a 100644
--- a/src/library_desugar/java/java/nio/file/FileApiFlips.java
+++ b/src/library_desugar/java/java/nio/file/FileApiFlips.java
@@ -101,32 +101,35 @@
throw exception("java.nio.file.attribute.FileTime", suffix);
}
+ public static Object flipMaybeFileTime(Object val) {
+ if (val instanceof j$.nio.file.attribute.FileTime) {
+ j$.nio.file.attribute.FileTime fileTime;
+ try {
+ fileTime = (j$.nio.file.attribute.FileTime) val;
+ } catch (ClassCastException cce) {
+ throw exceptionFileTime(cce);
+ }
+ return FileAttributeConversions.convert(fileTime);
+ }
+ if (val instanceof java.nio.file.attribute.FileTime) {
+ java.nio.file.attribute.FileTime fileTime;
+ try {
+ fileTime = (java.nio.file.attribute.FileTime) val;
+ } catch (ClassCastException cce) {
+ throw exceptionFileTime(cce);
+ }
+ return FileAttributeConversions.convert(fileTime);
+ }
+ return val;
+ }
+
public static Map<String, Object> flipMapWithMaybeFileTimeValues(Map<String, Object> in) {
if (in == null || in.isEmpty()) {
return in;
}
HashMap<String, Object> newMap = new HashMap<>();
for (String key : in.keySet()) {
- Object val = in.get(key);
- if (val instanceof j$.nio.file.attribute.FileTime) {
- j$.nio.file.attribute.FileTime fileTime;
- try {
- fileTime = (j$.nio.file.attribute.FileTime) val;
- } catch (ClassCastException cce) {
- throw exceptionFileTime(cce);
- }
- newMap.put(key, FileAttributeConversions.convert(fileTime));
- } else if (val instanceof java.nio.file.attribute.FileTime) {
- java.nio.file.attribute.FileTime fileTime;
- try {
- fileTime = (java.nio.file.attribute.FileTime) val;
- } catch (ClassCastException cce) {
- throw exceptionFileTime(cce);
- }
- newMap.put(key, FileAttributeConversions.convert(fileTime));
- } else {
- newMap.put(key, val);
- }
+ newMap.put(key, flipMaybeFileTime(in.get(key)));
}
return newMap;
}
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
index ff05a80..c97bd79 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
@@ -7,6 +7,13 @@
"common_flags": [
{
"api_level_below_or_equal": 10000,
+ "amend_library_method": [
+ "public static java.nio.file.Path java.nio.file.Path#of(java.lang.String, java.lang.String[])",
+ "public static java.nio.file.Path java.nio.file.Path#of(java.net.URI)"
+ ]
+ },
+ {
+ "api_level_below_or_equal": 10000,
"api_level_greater_or_equal": 24,
"rewrite_prefix": {
"java.util.stream.DesugarDoubleStream": "j$.util.stream.DesugarDoubleStream",
@@ -35,13 +42,15 @@
"long java.util.concurrent.TimeUnit#convert(java.time.Duration)": "java.util.concurrent.DesugarTimeUnit"
},
"retarget_method_with_emulated_dispatch": {
- "long java.io.InputStream#transferTo(java.io.OutputStream)": "java.io.DesugarInputStream"
+ "long java.io.InputStream#transferTo(java.io.OutputStream)": "java.io.DesugarInputStream",
+ "long java.io.ByteArrayInputStream#transferTo(java.io.OutputStream)": "long java.io.DesugarInputStream#transferTo(java.io.InputStream, java.io.OutputStream)"
},
"amend_library_method": [
"public static java.util.concurrent.TimeUnit java.util.concurrent.TimeUnit#of(java.time.temporal.ChronoUnit)",
"public java.time.temporal.ChronoUnit java.util.concurrent.TimeUnit#toChronoUnit()",
"public long java.util.concurrent.TimeUnit#convert(java.time.Duration)",
- "public long java.io.InputStream#transferTo(java.io.OutputStream)"
+ "public long java.io.InputStream#transferTo(java.io.OutputStream)",
+ "public long java.io.ByteArrayInputStream#transferTo(java.io.OutputStream)"
]
},
{
@@ -220,7 +229,8 @@
"java.util.Map java.nio.file.spi.FileSystemProvider#readAttributes(java.nio.file.Path, java.lang.String, java.nio.file.LinkOption[])" : [-1, "java.util.Map java.nio.file.FileApiFlips#flipMapWithMaybeFileTimeValues(java.util.Map)"],
"java.lang.Iterable java.nio.file.FileSystem#getRootDirectories()": [-1, "java.lang.Iterable java.nio.file.PathApiFlips#flipIterablePath(java.lang.Iterable)"],
"java.util.Iterator java.nio.file.Path#iterator()": [-1, "java.util.Iterator java.nio.file.PathApiFlips#flipIteratorPath(java.util.Iterator)"],
- "java.nio.file.DirectoryStream java.nio.file.spi.FileSystemProvider#newDirectoryStream(java.nio.file.Path, java.nio.file.DirectoryStream$Filter)": [-1, "java.nio.file.DirectoryStream java.nio.file.PathApiFlips#flipDirectoryStreamPath(java.nio.file.DirectoryStream)", 1, "java.nio.file.DirectoryStream$Filter java.nio.file.PathApiFlips#flipDirectoryStreamFilterPath(java.nio.file.DirectoryStream$Filter)"]
+ "java.nio.file.DirectoryStream java.nio.file.spi.FileSystemProvider#newDirectoryStream(java.nio.file.Path, java.nio.file.DirectoryStream$Filter)": [-1, "java.nio.file.DirectoryStream java.nio.file.PathApiFlips#flipDirectoryStreamPath(java.nio.file.DirectoryStream)", 1, "java.nio.file.DirectoryStream$Filter java.nio.file.PathApiFlips#flipDirectoryStreamFilterPath(java.nio.file.DirectoryStream$Filter)"],
+ "void java.nio.file.spi.FileSystemProvider#setAttribute(java.nio.file.Path, java.lang.String, java.lang.Object, java.nio.file.LinkOption[])": [2, "java.lang.Object java.nio.file.FileApiFlips#flipMaybeFileTime(java.lang.Object)"]
},
"wrapper_conversion": [
"java.nio.channels.CompletionHandler",
@@ -403,6 +413,14 @@
]
},
{
+ "api_level_below_or_equal": 10000,
+ "api_level_greater_or_equal": 26,
+ "retarget_method": {
+ "java.nio.file.Path java.nio.file.Path#of(java.lang.String, java.lang.String[])": "java.nio.file.Path java.nio.file.Paths#get(java.lang.String, java.lang.String[])",
+ "java.nio.file.Path java.nio.file.Path#of(java.net.URI)": "java.nio.file.Path java.nio.file.Paths#get(java.net.URI)"
+ }
+ },
+ {
"api_level_below_or_equal": 33,
"amend_library_method": [
"public java.lang.Object[] java.util.Collection#toArray(java.util.function.IntFunction)"
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index 249de08..84334f5 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -131,7 +131,8 @@
String owner = namingLens.lookupInternalName(rewrittenMethod.holder);
String name = namingLens.lookupName(rewrittenMethod).toString();
String desc = rewrittenMethod.proto.toDescriptorString(namingLens);
- visitor.visitMethodInsn(rewrittenType.getCfOpcode(), owner, name, desc, itf);
+ visitor.visitMethodInsn(
+ rewrittenType.getCfOpcode(), owner, name, desc, rewrittenType.isInterface() || itf);
}
}
diff --git a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepEdge.java b/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepEdge.java
deleted file mode 100644
index f61483a..0000000
--- a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepEdge.java
+++ /dev/null
@@ -1,57 +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.experimental.keepanno.ast;
-
-/**
- * An edge in the keep graph.
- *
- * <p>An edge describes a set of preconditions and a set of consequences. If the preconditions are
- * met, then the consequences are put into effect.
- */
-public final class KeepEdge {
-
- public static class Builder {
- private KeepPreconditions preconditions = KeepPreconditions.always();
- private KeepConsequences consequences;
-
- private Builder() {}
-
- public Builder setPreconditions(KeepPreconditions preconditions) {
- this.preconditions = preconditions;
- return this;
- }
-
- public Builder setConsequences(KeepConsequences consequences) {
- this.consequences = consequences;
- return this;
- }
-
- public KeepEdge build() {
- if (consequences.isEmpty()) {
- throw new KeepEdgeException("KeepEdge must have non-empty set of consequences.");
- }
- return new KeepEdge(preconditions, consequences);
- }
- }
-
- public static Builder builder() {
- return new Builder();
- }
-
- private final KeepPreconditions preconditions;
- private final KeepConsequences consequences;
-
- private KeepEdge(KeepPreconditions preconditions, KeepConsequences consequences) {
- this.preconditions = preconditions;
- this.consequences = consequences;
- }
-
- public KeepPreconditions getPreconditions() {
- return preconditions;
- }
-
- public KeepConsequences getConsequences() {
- return consequences;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodAccessPattern.java b/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodAccessPattern.java
deleted file mode 100644
index 7f83937..0000000
--- a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepMethodAccessPattern.java
+++ /dev/null
@@ -1,16 +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.experimental.keepanno.ast;
-
-// TODO: finish this.
-public class KeepMethodAccessPattern {
-
- public static KeepMethodAccessPattern any() {
- return new KeepMethodAccessPattern();
- }
-
- public boolean isAny() {
- return true;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepOptions.java b/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepOptions.java
deleted file mode 100644
index 9fb521c..0000000
--- a/src/main/java/com/android/tools/r8/experimental/keepanno/ast/KeepOptions.java
+++ /dev/null
@@ -1,97 +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.experimental.keepanno.ast;
-
-import com.android.tools.r8.utils.SetUtils;
-import com.google.common.collect.ImmutableSet;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Set;
-
-public final class KeepOptions {
-
- public enum KeepOption {
- SHRINKING,
- OPTIMIZING,
- OBFUSCATING,
- ACCESS_MODIFING,
- }
-
- public static KeepOptions keepAll() {
- if (ALLOW_NONE_INSTANCE == null) {
- ALLOW_NONE_INSTANCE = new KeepOptions(true, Collections.emptySet());
- }
- return ALLOW_NONE_INSTANCE;
- }
-
- public static Builder allowBuilder() {
- return new Builder(true);
- }
-
- public static Builder disallowBuilder() {
- return new Builder(false);
- }
-
- public static KeepOptions allow(KeepOption... options) {
- return allowBuilder().addAll(options).build();
- }
-
- public static KeepOptions disallow(KeepOption... options) {
- return disallowBuilder().addAll(options).build();
- }
-
- public static class Builder {
- public final boolean allowIfSet;
- public Set<KeepOption> options = SetUtils.newIdentityHashSet();
-
- private Builder(boolean allowIfSet) {
- this.allowIfSet = allowIfSet;
- }
-
- public Builder add(KeepOption option) {
- options.add(option);
- return this;
- }
-
- public Builder addAll(KeepOption... options) {
- return addAll(Arrays.asList(options));
- }
-
- public Builder addAll(Collection<KeepOption> options) {
- this.options.addAll(options);
- return this;
- }
-
- public KeepOptions build() {
- if (options.isEmpty()) {
- if (allowIfSet) {
- return keepAll();
- }
- throw new KeepEdgeException("Invalid keep options that disallow nothing.");
- }
- if (options.size() == KeepOption.values().length) {
- if (!allowIfSet) {
- return keepAll();
- }
- throw new KeepEdgeException("Invalid keep options that allow everything.");
- }
- return new KeepOptions(allowIfSet, ImmutableSet.copyOf(options));
- }
- }
-
- private static KeepOptions ALLOW_NONE_INSTANCE = null;
-
- private final boolean allowIfSet;
- private final Set<KeepOption> options;
-
- private KeepOptions(boolean allowIfSet, Set<KeepOption> options) {
- this.allowIfSet = allowIfSet;
- this.options = options;
- }
-
- public boolean isAllowed(KeepOption option) {
- return options.contains(option) == allowIfSet;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/graph/AccessFlags.java b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
index 10620fc6..ee86680 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
@@ -109,25 +109,14 @@
}
public boolean isMoreVisibleThan(
- AccessFlags other, String packageNameThis, String packageNameOther) {
+ AccessFlags<?> other, String packageNameThis, String packageNameOther) {
int visibilityOrdinal = getVisibilityOrdinal();
if (visibilityOrdinal > other.getVisibilityOrdinal()) {
return true;
}
- if (visibilityOrdinal == other.getVisibilityOrdinal()
+ return visibilityOrdinal == other.getVisibilityOrdinal()
&& isVisibilityDependingOnPackage()
- && !packageNameThis.equals(packageNameOther)) {
- return true;
- }
- return false;
- }
-
- public boolean isAtLeastAsVisibleAs(AccessFlags other) {
- return getVisibilityOrdinal() >= other.getVisibilityOrdinal();
- }
-
- public boolean isSameVisibility(AccessFlags other) {
- return getVisibilityOrdinal() == other.getVisibilityOrdinal();
+ && !packageNameThis.equals(packageNameOther);
}
public int getVisibilityOrdinal() {
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
index 42f5d96..369e60c 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -536,7 +536,7 @@
assert potentialHolder.isInterface();
for (DexEncodedMethod virtualMethod : potentialHolder.virtualMethods()) {
if (virtualMethod.getReference().match(method.getReference())
- && virtualMethod.accessFlags.isSameVisibility(method.accessFlags)) {
+ && virtualMethod.isSameVisibility(method)) {
return true;
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index d68d906..985e215 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -569,6 +569,40 @@
return isStatic();
}
+ public boolean isAtLeastAsVisibleAsOtherInSameHierarchy(
+ DexEncodedMethod other, AppView<? extends AppInfoWithClassHierarchy> appView) {
+ assert getReference().getProto() == other.getReference().getProto();
+ assert appView.isSubtype(getHolderType(), other.getHolderType()).isTrue()
+ || appView.isSubtype(other.getHolderType(), getHolderType()).isTrue();
+ AccessFlags<MethodAccessFlags> accessFlags = getAccessFlags();
+ AccessFlags<?> otherAccessFlags = other.getAccessFlags();
+ if (accessFlags.getVisibilityOrdinal() < otherAccessFlags.getVisibilityOrdinal()) {
+ return false;
+ } else if (accessFlags.isPrivate()) {
+ return getHolderType() == other.getHolderType();
+ } else if (accessFlags.isPublic() || accessFlags.isProtected()) {
+ return true;
+ } else {
+ assert accessFlags.isPackagePrivate();
+ return getHolderType().getPackageName().equals(other.getHolderType().getPackageName());
+ }
+ }
+
+ public boolean isSameVisibility(DexEncodedMethod other) {
+ AccessFlags<MethodAccessFlags> accessFlags = getAccessFlags();
+ if (accessFlags.getVisibilityOrdinal() != other.getAccessFlags().getVisibilityOrdinal()) {
+ return false;
+ }
+ if (accessFlags.isPublic()) {
+ return true;
+ }
+ if (accessFlags.isPrivate()) {
+ return getHolderType() == other.getHolderType();
+ }
+ assert accessFlags.isVisibilityDependingOnPackage();
+ return getHolderType().getPackageName().equals(other.getHolderType().getPackageName());
+ }
+
/**
* Returns true if this method is synthetic.
*/
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
index 16c578b..8f7f2b6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
@@ -302,9 +302,26 @@
if (jsonFlagSet.has(RETARGET_METHOD_KEY)) {
for (Map.Entry<String, JsonElement> retarget :
jsonFlagSet.get(RETARGET_METHOD_KEY).getAsJsonObject().entrySet()) {
- builder.retargetMethod(
- parseMethod(retarget.getKey()),
- stringDescriptorToDexType(retarget.getValue().getAsString()));
+ String key = retarget.getKey();
+ String value = retarget.getValue().getAsString();
+ if (value.contains("#")) {
+ builder.retargetMethodToMethod(parseMethod(key), parseMethod(value));
+ } else {
+ builder.retargetMethodToType(parseMethod(key), stringDescriptorToDexType(value));
+ }
+ }
+ }
+ if (jsonFlagSet.has(RETARGET_METHOD_EMULATED_DISPATCH_KEY)) {
+ for (Map.Entry<String, JsonElement> retarget :
+ jsonFlagSet.get(RETARGET_METHOD_EMULATED_DISPATCH_KEY).getAsJsonObject().entrySet()) {
+ String key = retarget.getKey();
+ String value = retarget.getValue().getAsString();
+ if (value.contains("#")) {
+ builder.retargetMethodEmulatedDispatchToMethod(parseMethod(key), parseMethod(value));
+ } else {
+ builder.retargetMethodEmulatedDispatchToType(
+ parseMethod(key), stringDescriptorToDexType(value));
+ }
}
}
if (jsonFlagSet.has(COVARIANT_RETARGET_METHOD_KEY)) {
@@ -315,14 +332,6 @@
stringDescriptorToDexType(retarget.getValue().getAsString()));
}
}
- if (jsonFlagSet.has(RETARGET_METHOD_EMULATED_DISPATCH_KEY)) {
- for (Map.Entry<String, JsonElement> retarget :
- jsonFlagSet.get(RETARGET_METHOD_EMULATED_DISPATCH_KEY).getAsJsonObject().entrySet()) {
- builder.retargetMethodEmulatedDispatch(
- parseMethod(retarget.getKey()),
- stringDescriptorToDexType(retarget.getValue().getAsString()));
- }
- }
if (jsonFlagSet.has(BACKPORT_KEY)) {
for (Map.Entry<String, JsonElement> backport :
jsonFlagSet.get(BACKPORT_KEY).getAsJsonObject().entrySet()) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
index 8c4316f..314d657 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
@@ -32,8 +32,10 @@
private final Map<DexType, DexType> emulatedInterfaces;
private final Map<DexField, DexField> retargetStaticField;
private final Map<DexMethod, DexType> covariantRetarget;
- private final Map<DexMethod, DexType> retargetMethod;
- private final Map<DexMethod, DexType> retargetMethodEmulatedDispatch;
+ private final Map<DexMethod, DexType> retargetMethodToType;
+ private final Map<DexMethod, DexType> retargetMethodEmulatedDispatchToType;
+ private final Map<DexMethod, DexMethod> retargetMethodToMethod;
+ private final Map<DexMethod, DexMethod> retargetMethodEmulatedDispatchToMethod;
private final Map<DexMethod, DexMethod[]> apiGenericTypesConversion;
private final Map<DexType, DexType> legacyBackport;
private final Map<DexType, DexType> customConversions;
@@ -52,8 +54,10 @@
Map<DexType, DexType> emulateLibraryInterface,
Map<DexField, DexField> retargetStaticField,
Map<DexMethod, DexType> covariantRetarget,
- Map<DexMethod, DexType> retargetMethod,
- Map<DexMethod, DexType> retargetMethodEmulatedDispatch,
+ Map<DexMethod, DexType> retargetMethodToType,
+ Map<DexMethod, DexType> retargetMethodEmulatedDispatchToType,
+ Map<DexMethod, DexMethod> retargetMethodToMethod,
+ Map<DexMethod, DexMethod> retargetMethodEmulatedDispatchToMethod,
Map<DexMethod, DexMethod[]> apiGenericTypesConversion,
Map<DexType, DexType> legacyBackport,
Map<DexType, DexType> customConversion,
@@ -70,8 +74,10 @@
this.emulatedInterfaces = emulateLibraryInterface;
this.retargetStaticField = retargetStaticField;
this.covariantRetarget = covariantRetarget;
- this.retargetMethod = retargetMethod;
- this.retargetMethodEmulatedDispatch = retargetMethodEmulatedDispatch;
+ this.retargetMethodToType = retargetMethodToType;
+ this.retargetMethodEmulatedDispatchToType = retargetMethodEmulatedDispatchToType;
+ this.retargetMethodToMethod = retargetMethodToMethod;
+ this.retargetMethodEmulatedDispatchToMethod = retargetMethodEmulatedDispatchToMethod;
this.apiGenericTypesConversion = apiGenericTypesConversion;
this.legacyBackport = legacyBackport;
this.customConversions = customConversion;
@@ -97,6 +103,8 @@
ImmutableMap.of(),
ImmutableMap.of(),
ImmutableMap.of(),
+ ImmutableMap.of(),
+ ImmutableMap.of(),
ImmutableSet.of(),
ImmutableSet.of(),
ImmutableMap.of(),
@@ -120,8 +128,10 @@
emulatedInterfaces,
retargetStaticField,
covariantRetarget,
- retargetMethod,
- retargetMethodEmulatedDispatch,
+ retargetMethodToType,
+ retargetMethodEmulatedDispatchToType,
+ retargetMethodToMethod,
+ retargetMethodEmulatedDispatchToMethod,
apiGenericTypesConversion,
legacyBackport,
customConversions,
@@ -161,12 +171,20 @@
return covariantRetarget;
}
- public Map<DexMethod, DexType> getRetargetMethod() {
- return retargetMethod;
+ public Map<DexMethod, DexType> getRetargetMethodToType() {
+ return retargetMethodToType;
}
- public Map<DexMethod, DexType> getRetargetMethodEmulatedDispatch() {
- return retargetMethodEmulatedDispatch;
+ public Map<DexMethod, DexType> getRetargetMethodEmulatedDispatchToType() {
+ return retargetMethodEmulatedDispatchToType;
+ }
+
+ public Map<DexMethod, DexMethod> getRetargetMethodToMethod() {
+ return retargetMethodToMethod;
+ }
+
+ public Map<DexMethod, DexMethod> getRetargetMethodEmulatedDispatchToMethod() {
+ return retargetMethodEmulatedDispatchToMethod;
}
public Set<DexMethod> getNeverOutlineApi() {
@@ -211,8 +229,8 @@
&& maintainPrefix.isEmpty()
&& emulatedInterfaces.isEmpty()
&& covariantRetarget.isEmpty()
- && retargetMethod.isEmpty()
- && retargetMethodEmulatedDispatch.isEmpty()
+ && retargetMethodToType.isEmpty()
+ && retargetMethodEmulatedDispatchToType.isEmpty()
&& retargetStaticField.isEmpty();
}
@@ -228,8 +246,10 @@
private final Map<DexType, DexType> emulatedInterfaces;
private final Map<DexField, DexField> retargetStaticField;
private final Map<DexMethod, DexType> covariantRetarget;
- private final Map<DexMethod, DexType> retargetMethod;
- private final Map<DexMethod, DexType> retargetMethodEmulatedDispatch;
+ private final Map<DexMethod, DexType> retargetMethodToType;
+ private final Map<DexMethod, DexType> retargetMethodEmulatedDispatchToType;
+ private final Map<DexMethod, DexMethod> retargetMethodToMethod;
+ private final Map<DexMethod, DexMethod> retargetMethodEmulatedDispatchToMethod;
private final Map<DexMethod, DexMethod[]> apiGenericTypesConversion;
private final Map<DexType, DexType> legacyBackport;
private final Map<DexType, DexType> customConversions;
@@ -256,6 +276,8 @@
new IdentityHashMap<>(),
new IdentityHashMap<>(),
new IdentityHashMap<>(),
+ new IdentityHashMap<>(),
+ new IdentityHashMap<>(),
Sets.newIdentityHashSet(),
Sets.newIdentityHashSet(),
new IdentityHashMap<>(),
@@ -274,8 +296,10 @@
Map<DexType, DexType> emulateLibraryInterface,
Map<DexField, DexField> retargetStaticField,
Map<DexMethod, DexType> covariantRetarget,
- Map<DexMethod, DexType> retargetMethod,
- Map<DexMethod, DexType> retargetMethodEmulatedDispatch,
+ Map<DexMethod, DexType> retargetMethodToType,
+ Map<DexMethod, DexType> retargetMethodEmulatedDispatchToType,
+ Map<DexMethod, DexMethod> retargetMethodToMethod,
+ Map<DexMethod, DexMethod> retargetMethodEmulatedDispatchToMethod,
Map<DexMethod, DexMethod[]> apiConversionCollection,
Map<DexType, DexType> backportCoreLibraryMember,
Map<DexType, DexType> customConversions,
@@ -294,8 +318,12 @@
this.emulatedInterfaces = new IdentityHashMap<>(emulateLibraryInterface);
this.retargetStaticField = new IdentityHashMap<>(retargetStaticField);
this.covariantRetarget = new IdentityHashMap<>(covariantRetarget);
- this.retargetMethod = new IdentityHashMap<>(retargetMethod);
- this.retargetMethodEmulatedDispatch = new IdentityHashMap<>(retargetMethodEmulatedDispatch);
+ this.retargetMethodToType = new IdentityHashMap<>(retargetMethodToType);
+ this.retargetMethodEmulatedDispatchToType =
+ new IdentityHashMap<>(retargetMethodEmulatedDispatchToType);
+ this.retargetMethodToMethod = new IdentityHashMap<>(retargetMethodToMethod);
+ this.retargetMethodEmulatedDispatchToMethod =
+ new IdentityHashMap<>(retargetMethodEmulatedDispatchToMethod);
this.apiGenericTypesConversion = new IdentityHashMap<>(apiConversionCollection);
this.legacyBackport = new IdentityHashMap<>(backportCoreLibraryMember);
this.customConversions = new IdentityHashMap<>(customConversions);
@@ -384,15 +412,42 @@
return this;
}
- public Builder retargetMethod(DexMethod key, DexType rewrittenType) {
+ public Builder retargetMethodToType(DexMethod key, DexType rewrittenType) {
put(
- retargetMethod,
+ retargetMethodToType,
key,
rewrittenType,
HumanDesugaredLibrarySpecificationParser.RETARGET_METHOD_KEY);
return this;
}
+ public Builder retargetMethodEmulatedDispatchToType(DexMethod key, DexType rewrittenType) {
+ put(
+ retargetMethodEmulatedDispatchToType,
+ key,
+ rewrittenType,
+ HumanDesugaredLibrarySpecificationParser.RETARGET_METHOD_EMULATED_DISPATCH_KEY);
+ return this;
+ }
+
+ public Builder retargetMethodToMethod(DexMethod key, DexMethod retarget) {
+ put(
+ retargetMethodToMethod,
+ key,
+ retarget,
+ HumanDesugaredLibrarySpecificationParser.RETARGET_METHOD_KEY);
+ return this;
+ }
+
+ public Builder retargetMethodEmulatedDispatchToMethod(DexMethod key, DexMethod retarget) {
+ put(
+ retargetMethodEmulatedDispatchToMethod,
+ key,
+ retarget,
+ HumanDesugaredLibrarySpecificationParser.RETARGET_METHOD_EMULATED_DISPATCH_KEY);
+ return this;
+ }
+
public Builder covariantRetargetMethod(DexMethod key, DexType rewrittenType) {
put(
covariantRetarget,
@@ -411,15 +466,6 @@
return this;
}
- public Builder retargetMethodEmulatedDispatch(DexMethod key, DexType rewrittenType) {
- put(
- retargetMethodEmulatedDispatch,
- key,
- rewrittenType,
- HumanDesugaredLibrarySpecificationParser.RETARGET_METHOD_EMULATED_DISPATCH_KEY);
- return this;
- }
-
public void addApiGenericTypesConversion(DexMethod method, int index, DexMethod conversion) {
DexMethod[] types =
apiGenericTypesConversion.computeIfAbsent(
@@ -473,8 +519,10 @@
ImmutableMap.copyOf(emulatedInterfaces),
ImmutableMap.copyOf(retargetStaticField),
ImmutableMap.copyOf(covariantRetarget),
- ImmutableMap.copyOf(retargetMethod),
- ImmutableMap.copyOf(retargetMethodEmulatedDispatch),
+ ImmutableMap.copyOf(retargetMethodToType),
+ ImmutableMap.copyOf(retargetMethodEmulatedDispatchToType),
+ ImmutableMap.copyOf(retargetMethodToMethod),
+ ImmutableMap.copyOf(retargetMethodEmulatedDispatchToMethod),
ImmutableMap.copyOf(apiGenericTypesConversion),
ImmutableMap.copyOf(legacyBackport),
ImmutableMap.copyOf(customConversions),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java
index 6283419..ad9735c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationFlagDeduplicator.java
@@ -93,15 +93,15 @@
commonBuilder::putEmulatedInterface,
builder::putEmulatedInterface);
deduplicateFlags(
- flags.getRetargetMethod(),
- otherFlags.getRetargetMethod(),
- commonBuilder::retargetMethod,
- builder::retargetMethod);
+ flags.getRetargetMethodToType(),
+ otherFlags.getRetargetMethodToType(),
+ commonBuilder::retargetMethodToType,
+ builder::retargetMethodToType);
deduplicateFlags(
- flags.getRetargetMethodEmulatedDispatch(),
- otherFlags.getRetargetMethodEmulatedDispatch(),
- commonBuilder::retargetMethodEmulatedDispatch,
- builder::retargetMethodEmulatedDispatch);
+ flags.getRetargetMethodEmulatedDispatchToType(),
+ otherFlags.getRetargetMethodEmulatedDispatchToType(),
+ commonBuilder::retargetMethodEmulatedDispatchToType,
+ builder::retargetMethodEmulatedDispatchToType);
deduplicateFlags(
flags.getLegacyBackport(),
otherFlags.getLegacyBackport(),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java
index 736632a..2096b33 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java
@@ -124,13 +124,22 @@
if (!flags.getRetargetStaticField().isEmpty()) {
toJson.put(RETARGET_STATIC_FIELD_KEY, mapToString(flags.getRetargetStaticField()));
}
- if (!flags.getRetargetMethod().isEmpty()) {
- toJson.put(RETARGET_METHOD_KEY, mapToString(flags.getRetargetMethod()));
+ if (!flags.getRetargetMethodToType().isEmpty()) {
+ toJson.put(RETARGET_METHOD_KEY, mapToString(flags.getRetargetMethodToType()));
}
- if (!flags.getRetargetMethodEmulatedDispatch().isEmpty()) {
+ if (!flags.getRetargetMethodEmulatedDispatchToMethod().isEmpty()) {
+ toJson.put(
+ RETARGET_METHOD_KEY, mapToString(flags.getRetargetMethodEmulatedDispatchToMethod()));
+ }
+ if (!flags.getRetargetMethodEmulatedDispatchToType().isEmpty()) {
toJson.put(
RETARGET_METHOD_EMULATED_DISPATCH_KEY,
- mapToString(flags.getRetargetMethodEmulatedDispatch()));
+ mapToString(flags.getRetargetMethodEmulatedDispatchToType()));
+ }
+ if (!flags.getRetargetMethodEmulatedDispatchToMethod().isEmpty()) {
+ toJson.put(
+ RETARGET_METHOD_EMULATED_DISPATCH_KEY,
+ mapToString(flags.getRetargetMethodEmulatedDispatchToMethod()));
}
if (!flags.getDontRetarget().isEmpty()) {
toJson.put(DONT_RETARGET_KEY, setToString(flags.getDontRetarget()));
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java
index 5f385c5..00c7da1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineEmulatedInterfaceConverter.java
@@ -95,7 +95,7 @@
LinkedHashMap<DexType, DerivedMethod> extraDispatchCases = new LinkedHashMap<>();
// Retarget core lib emulated dispatch handled as part of emulated interface dispatch.
Map<DexMethod, DexType> retargetCoreLibMember =
- rewritingFlags.getRetargetMethodEmulatedDispatch();
+ rewritingFlags.getRetargetMethodEmulatedDispatchToType();
for (DexMethod retarget : retargetCoreLibMember.keySet()) {
if (retarget.match(method)) {
DexClass inClass = appInfo.definitionFor(retarget.getHolderType());
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
index a1a8d29..7a669c2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
@@ -50,11 +50,14 @@
public void convertPrefixFlags(
HumanRewritingFlags rewritingFlags, BiConsumer<String, Set<DexString>> warnConsumer) {
rewriteClasses();
- rewriteValues(rewritingFlags.getRetargetMethod());
- rewriteValues(rewritingFlags.getRetargetMethodEmulatedDispatch());
+ rewriteValues(rewritingFlags.getRetargetMethodToType());
+ rewriteValues(rewritingFlags.getRetargetMethodEmulatedDispatchToType());
rewriteValues(rewritingFlags.getCustomConversions());
+ rewriteMethodValues(rewritingFlags.getRetargetMethodToMethod());
+ rewriteMethodValues(rewritingFlags.getRetargetMethodEmulatedDispatchToMethod());
rewriteEmulatedInterface(rewritingFlags.getEmulatedInterfaces());
- rewriteRetargetKeys(rewritingFlags.getRetargetMethodEmulatedDispatch());
+ rewriteRetargetKeys(rewritingFlags.getRetargetMethodEmulatedDispatchToType());
+ rewriteRetargetKeys(rewritingFlags.getRetargetMethodEmulatedDispatchToMethod());
rewriteApiConversions(rewritingFlags.getApiGenericConversion());
warnIfUnusedPrefix(warnConsumer);
}
@@ -79,7 +82,7 @@
convertedPrefix + interfaceType.substring(firstPackage + 1)));
}
- private void rewriteRetargetKeys(Map<DexMethod, DexType> retarget) {
+ private void rewriteRetargetKeys(Map<DexMethod, ?> retarget) {
for (DexMethod dexMethod : retarget.keySet()) {
DexType type = convertJavaNameToDesugaredLibrary(dexMethod.holder);
builder.rewriteDerivedTypeOnly(dexMethod.holder, type);
@@ -108,6 +111,12 @@
}
}
+ private void rewriteMethodValues(Map<?, DexMethod> flags) {
+ for (DexMethod method : flags.values()) {
+ registerType(method.getHolderType());
+ }
+ }
+
private void rewriteClasses() {
appInfo.app().forEachLibraryType(this::registerClassType);
if (libraryCompilation) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
index 6078e9d..004c35f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
@@ -24,6 +24,7 @@
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
public class HumanToMachineRetargetConverter {
@@ -45,13 +46,22 @@
.getCovariantRetarget()
.forEach((method, type) -> convertCovariantRetarget(builder, method, type));
rewritingFlags
- .getRetargetMethod()
- .forEach((method, type) -> convertRetargetMethod(builder, method, type));
+ .getRetargetMethodToType()
+ .forEach((method, type) -> convertRetargetMethodToType(builder, method, type));
rewritingFlags
- .getRetargetMethodEmulatedDispatch()
+ .getRetargetMethodEmulatedDispatchToType()
.forEach(
(method, type) ->
- convertRetargetMethodEmulatedDispatch(builder, rewritingFlags, method, type));
+ convertRetargetMethodEmulatedDispatchToType(builder, rewritingFlags, method, type));
+ rewritingFlags
+ .getRetargetMethodToMethod()
+ .forEach((method, retarget) -> convertRetargetMethodToMethod(builder, method, retarget));
+ rewritingFlags
+ .getRetargetMethodEmulatedDispatchToMethod()
+ .forEach(
+ (method, retarget) ->
+ convertRetargetMethodEmulatedDispatchToMethod(
+ builder, rewritingFlags, method, retarget));
warnConsumer.accept("Cannot retarget missing references: ", missingReferences);
}
@@ -66,37 +76,6 @@
builder.putStaticFieldRetarget(field, rewrittenField);
}
- private void convertRetargetMethodEmulatedDispatch(
- MachineRewritingFlags.Builder builder,
- HumanRewritingFlags rewritingFlags,
- DexMethod method,
- DexType type) {
- DexClass holder = appInfo.definitionFor(method.holder);
- DexEncodedMethod foundMethod = holder.lookupMethod(method);
- if (foundMethod == null) {
- missingReferences.add(method);
- return;
- }
- if (foundMethod.isStatic()) {
- appInfo
- .app()
- .options
- .reporter
- .error("Cannot generate emulated dispatch for static method " + foundMethod);
- return;
- }
- if (!seemsToNeedEmulatedDispatch(holder, foundMethod)) {
- appInfo
- .app()
- .options
- .reporter
- .warning(
- "Generating (seemingly unnecessary) emulated dispatch for final method "
- + foundMethod);
- }
- convertEmulatedVirtualRetarget(builder, rewritingFlags, foundMethod, type);
- }
-
private void convertCovariantRetarget(
MachineRewritingFlags.Builder builder, DexMethod method, DexType type) {
DexClass holder = appInfo.definitionFor(method.holder);
@@ -133,7 +112,9 @@
}
private void convertRetargetMethod(
- MachineRewritingFlags.Builder builder, DexMethod method, DexType type) {
+ DexMethod method,
+ Consumer<DexEncodedMethod> staticRetarget,
+ Consumer<DexEncodedMethod> nonEmulatedVirtualRetarget) {
DexClass holder = appInfo.definitionFor(method.holder);
DexEncodedMethod foundMethod = holder.lookupMethod(method);
if (foundMethod == null) {
@@ -141,7 +122,7 @@
return;
}
if (foundMethod.isStatic()) {
- convertStaticRetarget(builder, foundMethod, type);
+ staticRetarget.accept(foundMethod);
return;
}
if (seemsToNeedEmulatedDispatch(holder, foundMethod)) {
@@ -154,7 +135,72 @@
+ foundMethod
+ " which could lead to invalid runtime execution in overrides.");
}
- convertNonEmulatedVirtualRetarget(builder, foundMethod, type);
+ nonEmulatedVirtualRetarget.accept(foundMethod);
+ }
+
+ private void convertRetargetMethodToType(
+ MachineRewritingFlags.Builder builder, DexMethod method, DexType type) {
+ convertRetargetMethod(
+ method,
+ foundMethod -> convertStaticRetarget(builder, foundMethod, type),
+ foundMethod -> convertNonEmulatedVirtualRetarget(builder, foundMethod, type));
+ }
+
+ private void convertRetargetMethodToMethod(
+ MachineRewritingFlags.Builder builder, DexMethod method, DexMethod retarget) {
+ convertRetargetMethod(
+ method,
+ foundMethod -> builder.putStaticRetarget(method, retarget),
+ foundMethod -> builder.putNonEmulatedVirtualRetarget(method, retarget));
+ }
+
+ private void convertRetargetMethodEmulatedDispatch(
+ DexMethod method, Consumer<DexEncodedMethod> emulatedRetarget) {
+ DexClass holder = appInfo.definitionFor(method.holder);
+ DexEncodedMethod foundMethod = holder.lookupMethod(method);
+ if (foundMethod == null) {
+ missingReferences.add(method);
+ return;
+ }
+ if (foundMethod.isStatic()) {
+ appInfo
+ .app()
+ .options
+ .reporter
+ .error("Cannot generate emulated dispatch for static method " + foundMethod);
+ return;
+ }
+ if (!seemsToNeedEmulatedDispatch(holder, foundMethod)) {
+ appInfo
+ .app()
+ .options
+ .reporter
+ .warning(
+ "Generating (seemingly unnecessary) emulated dispatch for final method "
+ + foundMethod);
+ }
+ emulatedRetarget.accept(foundMethod);
+ }
+
+ private void convertRetargetMethodEmulatedDispatchToType(
+ MachineRewritingFlags.Builder builder,
+ HumanRewritingFlags rewritingFlags,
+ DexMethod method,
+ DexType type) {
+ convertRetargetMethodEmulatedDispatch(
+ method,
+ foundMethod -> convertEmulatedVirtualRetarget(builder, rewritingFlags, foundMethod, type));
+ }
+
+ private void convertRetargetMethodEmulatedDispatchToMethod(
+ MachineRewritingFlags.Builder builder,
+ HumanRewritingFlags rewritingFlags,
+ DexMethod method,
+ DexMethod retarget) {
+ convertRetargetMethodEmulatedDispatch(
+ method,
+ foundMethod ->
+ convertEmulatedVirtualRetarget(builder, rewritingFlags, foundMethod, retarget));
}
private boolean seemsToNeedEmulatedDispatch(DexClass holder, DexEncodedMethod method) {
@@ -166,10 +212,7 @@
MachineRewritingFlags.Builder builder,
HumanRewritingFlags rewritingFlags,
DexEncodedMethod src,
- DexType type) {
- DexProto newProto = appInfo.dexItemFactory().prependHolderToProto(src.getReference());
- DexMethod forwardingDexMethod =
- appInfo.dexItemFactory().createMethod(type, newProto, src.getName());
+ DexMethod forwardingDexMethod) {
if (isEmulatedInterfaceDispatch(src, appInfo, rewritingFlags)) {
// Handled by emulated interface dispatch.
builder.putEmulatedVirtualRetargetThroughEmulatedInterface(
@@ -190,6 +233,17 @@
interfaceMethod, dispatchMethod, forwardingMethod, dispatchCases));
}
+ private void convertEmulatedVirtualRetarget(
+ MachineRewritingFlags.Builder builder,
+ HumanRewritingFlags rewritingFlags,
+ DexEncodedMethod src,
+ DexType type) {
+ DexProto newProto = appInfo.dexItemFactory().prependHolderToProto(src.getReference());
+ DexMethod forwardingDexMethod =
+ appInfo.dexItemFactory().createMethod(type, newProto, src.getName());
+ convertEmulatedVirtualRetarget(builder, rewritingFlags, src, forwardingDexMethod);
+ }
+
private boolean isEmulatedInterfaceDispatch(
DexEncodedMethod method,
AppInfoWithClassHierarchy appInfo,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
index b251add..1f7fe89 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
@@ -161,7 +161,7 @@
itemFactory.createMethod(
itemFactory.createType(itemFactory.arraysDescriptor), proto, name);
DexType target = itemFactory.createType("Ljava/util/DesugarArrays;");
- builder.retargetMethod(source, target);
+ builder.retargetMethodToType(source, target);
builder.amendLibraryMethod(
source,
@@ -178,7 +178,7 @@
source =
itemFactory.createMethod(itemFactory.createType("Ljava/util/TimeZone;"), proto, name);
target = itemFactory.createType("Ljava/util/DesugarTimeZone;");
- builder.retargetMethod(source, target);
+ builder.retargetMethodToType(source, target);
}
// Required by
// https://github.com/google/desugar_jdk_libs/commit/485071cd09a3691549d065ba9e323d07edccf085.
@@ -272,9 +272,9 @@
if (definition.isStatic()
|| definition.isFinal()
|| dexClassAndMethod.getHolder().isFinal()) {
- builder.retargetMethod(dexClassAndMethod.getReference(), rewrittenType);
+ builder.retargetMethodToType(dexClassAndMethod.getReference(), rewrittenType);
} else {
- builder.retargetMethodEmulatedDispatch(
+ builder.retargetMethodEmulatedDispatchToType(
dexClassAndMethod.getReference(), rewrittenType);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 6099074..3d9b8d2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -2220,8 +2220,7 @@
InstructionListIterator it = block.listIterator(code);
while (it.hasNext()) {
Instruction instruction = it.next();
- if (instruction.getLocalInfo() != null
- || !allowNewFilledArrayConstruction(instruction)) {
+ if (instruction.getLocalInfo() != null || !allowNewFilledArrayConstruction(instruction)) {
continue;
}
NewArrayEmpty newArray = instruction.asNewArrayEmpty();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
index 8a023db..08f3912 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
@@ -395,8 +395,7 @@
.isPossiblyFalse()
|| !newResolutionResult
.getResolvedMethod()
- .getAccessFlags()
- .isAtLeastAsVisibleAs(resolutionResult.getResolvedMethod().getAccessFlags())
+ .isAtLeastAsVisibleAsOtherInSameHierarchy(resolutionResult.getResolvedMethod(), appView)
// isOverriding expects both arguments to be not private.
|| (!newResolutionResult.getResolvedMethod().isPrivateMethod()
&& !isOverriding(
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
index 0cc861f..0dbea46 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
@@ -86,7 +86,13 @@
}
private DexMethod getReboundMethodReference(DexMethod method) {
- return nonReboundMethodReferenceToDefinitionMap.getOrDefault(method, method);
+ DexMethod rebound = nonReboundMethodReferenceToDefinitionMap.get(method);
+ while (rebound != null) {
+ method = rebound;
+ rebound = nonReboundMethodReferenceToDefinitionMap.get(method);
+ }
+ assert method != rebound;
+ return method;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
index d0066e0..5cbb1b6 100644
--- a/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
@@ -10,12 +10,17 @@
import com.android.tools.r8.graph.DexEncodedMethod;
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;
+import com.android.tools.r8.graph.MethodResolutionResult.FailedResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind;
import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemovalLens;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.KeepMethodInfo;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -31,12 +36,7 @@
}
private DexClassAndMethod getTargetForRedundantBridge(ProgramMethod method) {
- // Clean-up the predicate check.
- if (appView.appInfo().isPinned(method.getReference())) {
- return null;
- }
DexEncodedMethod definition = method.getDefinition();
- // TODO(b/197490164): Remove if method is abstract.
BridgeInfo bridgeInfo = definition.getOptimizationInfo().getBridgeInfo();
boolean isBridge = definition.isBridge() || bridgeInfo != null;
if (!isBridge || definition.isAbstract()) {
@@ -59,18 +59,10 @@
if (targetMethod == null) {
return null;
}
- if (method.getAccessFlags().isPublic()) {
- if (!targetMethod.getAccessFlags().isPublic()) {
- return null;
- }
- } else {
- if (targetMethod.getAccessFlags().isProtected()
- && !targetMethod.getHolderType().isSamePackage(method.getHolderType())) {
- return null;
- }
- if (targetMethod.getAccessFlags().isPrivate()) {
- return null;
- }
+ if (!targetMethod
+ .getDefinition()
+ .isAtLeastAsVisibleAsOtherInSameHierarchy(method.getDefinition(), appView)) {
+ return null;
}
if (definition.isStatic()
&& method.getHolder().hasClassInitializer()
@@ -148,6 +140,16 @@
ProgramMethodSet bridgesToRemoveForClass = ProgramMethodSet.create();
clazz.forEachProgramMethod(
method -> {
+ KeepMethodInfo keepInfo = appView.getKeepInfo(method);
+ if (!keepInfo.isShrinkingAllowed(appView.options())
+ || !keepInfo.isOptimizationAllowed(appView.options())) {
+ return;
+ }
+ if (isRedundantAbstractBridge(method)) {
+ // Record that the redundant bridge should be removed.
+ bridgesToRemoveForClass.add(method);
+ return;
+ }
DexClassAndMethod target = getTargetForRedundantBridge(method);
if (target != null) {
// Record that the redundant bridge should be removed.
@@ -175,6 +177,55 @@
return bridgesToRemove;
}
+ private boolean isRedundantAbstractBridge(ProgramMethod method) {
+ if (!method.getAccessFlags().isAbstract() || method.getDefinition().getCode() != null) {
+ return false;
+ }
+ DexProgramClass holder = method.getHolder();
+ if (holder.getSuperType() == null) {
+ assert holder.getType() == appView.dexItemFactory().objectType;
+ return false;
+ }
+ MethodResolutionResult superTypeResolution =
+ appView.appInfo().resolveMethodOn(holder.getSuperType(), method.getReference(), false);
+ if (superTypeResolution.isMultiMethodResolutionResult()) {
+ return false;
+ }
+ // Check if there is a definition in the super type hieararchy that is also abstract and has the
+ // same visibility.
+ if (superTypeResolution.isSingleResolution()) {
+ DexClassAndMethod resolutionPair =
+ superTypeResolution.asSingleResolution().getResolutionPair();
+ return resolutionPair.getDefinition().isAbstract()
+ && resolutionPair
+ .getDefinition()
+ .isAtLeastAsVisibleAsOtherInSameHierarchy(method.getDefinition(), appView)
+ && (!resolutionPair.getHolder().isInterface() || holder.getInterfaces().isEmpty());
+ }
+ // Only check for interfaces if resolving the method on super type causes NoSuchMethodError.
+ FailedResolutionResult failedResolutionResult = superTypeResolution.asFailedResolution();
+ if (failedResolutionResult == null
+ || !failedResolutionResult.isNoSuchMethodErrorResult(holder, appView.appInfo())
+ || holder.getInterfaces().isEmpty()) {
+ return false;
+ }
+ for (DexType iface : holder.getInterfaces()) {
+ SingleResolutionResult<?> singleIfaceResult =
+ appView
+ .appInfo()
+ .resolveMethodOn(iface, method.getReference(), true)
+ .asSingleResolution();
+ if (singleIfaceResult == null
+ || !singleIfaceResult.getResolvedMethod().isAbstract()
+ || !singleIfaceResult
+ .getResolvedMethod()
+ .isAtLeastAsVisibleAsOtherInSameHierarchy(method.getDefinition(), appView)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private void pruneApp(
Map<DexProgramClass, ProgramMethodSet> bridgesToRemove, ExecutorService executorService)
throws ExecutionException {
diff --git a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalLens.java b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalLens.java
index a680b36..042cc6a 100644
--- a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalLens.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
+import com.android.tools.r8.ir.code.Invoke;
import com.google.common.collect.Sets;
import java.util.IdentityHashMap;
import java.util.Map;
@@ -80,14 +81,18 @@
do {
newReference = methodMap.get(newReference);
} while (methodMap.containsKey(newReference));
- if (previous.getType().isSuper() && interfaces.contains(newReference.getHolderType())) {
+ boolean holderTypeIsInterface = interfaces.contains(newReference.getHolderType());
+ if (previous.getType().isSuper() && holderTypeIsInterface) {
return previous;
}
return MethodLookupResult.builder(this)
.setReference(newReference)
.setReboundReference(newReference)
.setPrototypeChanges(previous.getPrototypeChanges())
- .setType(previous.getType())
+ .setType(
+ holderTypeIsInterface && previous.getType().isVirtual()
+ ? Invoke.Type.INTERFACE
+ : previous.getType())
.build();
}
return previous;
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 9af4f06..e653a7a 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1548,7 +1548,8 @@
private boolean enableInterfaceMerging =
System.getProperty("com.android.tools.r8.enableHorizontalInterfaceMerging") != null;
private boolean enableInterfaceMergingInInitial = false;
- private boolean enableSameFilePolicy = false;
+ private boolean enableSameFilePolicy =
+ System.getProperty("com.android.tools.r8.enableSameFilePolicy") != null;
private boolean enableSyntheticMerging = true;
private boolean ignoreRuntimeTypeChecksForTesting = false;
private boolean restrictToSynthetics = false;
diff --git a/src/main/java/com/android/tools/r8/utils/ZipUtils.java b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
index fc9157d..43b1b02 100644
--- a/src/main/java/com/android/tools/r8/utils/ZipUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.ResourceException;
import com.android.tools.r8.androidapi.AndroidApiDataAccess;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.references.ClassReference;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;
import java.io.BufferedOutputStream;
@@ -304,6 +305,10 @@
return DescriptorUtils.getClassBinaryName(clazz) + CLASS_EXTENSION;
}
+ public static String zipEntryNameForClass(ClassReference clazz) {
+ return clazz.getBinaryName() + CLASS_EXTENSION;
+ }
+
public static long getOffsetOfResourceInZip(File file, String entry) throws IOException {
// Look into the jar file to see find the offset.
ZipFile zipFile = new ZipFile(file);
diff --git a/src/test/examplesJava11/path/PathExample.java b/src/test/examplesJava11/path/PathExample.java
new file mode 100644
index 0000000..8b17da2
--- /dev/null
+++ b/src/test/examplesJava11/path/PathExample.java
@@ -0,0 +1,16 @@
+// 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 path;
+
+import java.nio.file.Path;
+
+public class PathExample {
+
+ public static void main(String[] args) {
+ Path thePath = Path.of("foo", "bar");
+ System.out.println(thePath);
+ System.out.println(Path.of(thePath.toUri()).getFileName());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/JavaCompilerTool.java b/src/test/java/com/android/tools/r8/JavaCompilerTool.java
index b961367..ba23dd5 100644
--- a/src/test/java/com/android/tools/r8/JavaCompilerTool.java
+++ b/src/test/java/com/android/tools/r8/JavaCompilerTool.java
@@ -102,10 +102,10 @@
}
public JavaCompilerTool addAnnotationProcessors(String... processors) {
- return addAnnotationProcessor(Arrays.asList(processors));
+ return addAnnotationProcessors(Arrays.asList(processors));
}
- public JavaCompilerTool addAnnotationProcessor(Collection<String> processors) {
+ public JavaCompilerTool addAnnotationProcessors(Collection<String> processors) {
annotationProcessors.addAll(processors);
return this;
}
@@ -118,6 +118,11 @@
return this;
}
+ public JavaCompilerTool addDebugAgent() {
+ return addOptions(
+ "-J-debug", "-J-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005");
+ }
+
private Path getOrCreateOutputPath() throws IOException {
return output != null ? output : state.getNewTempFolder().resolve("out.jar");
}
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/BridgeWithInvokeSuperOnInterfaceTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/BridgeWithInvokeSuperOnInterfaceTest.java
new file mode 100644
index 0000000..cc24115
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/BridgeWithInvokeSuperOnInterfaceTest.java
@@ -0,0 +1,107 @@
+// 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.bridgeremoval;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+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 BridgeWithInvokeSuperOnInterfaceTest extends TestBase {
+
+ private static final String[] EXPECTED = new String[] {"I::foo"};
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(Main.class, I.class)
+ .addProgramClassFileData(getJWithBridgeAccessFlag())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class, I.class)
+ .addProgramClassFileData(getJWithBridgeAccessFlag())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .enableNoVerticalClassMergingAnnotations()
+ .enableInliningAnnotations()
+ .addDontObfuscate()
+ .compile()
+ .inspect(
+ inspector -> {
+ // Check that we are removing the bridge if we support default methods.
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ ClassSubject J = inspector.clazz(J.class);
+ assertThat(J, isPresent());
+ MethodSubject fooMethod = J.uniqueMethodWithOriginalName("foo");
+ assertThat(fooMethod, not(isPresent()));
+ }
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ private byte[] getJWithBridgeAccessFlag() throws Exception {
+ return transformer(J.class)
+ .setAccessFlags(MethodPredicate.onName("foo"), MethodAccessFlags::setBridge)
+ .transform();
+ }
+
+ @NoVerticalClassMerging
+ public interface I {
+
+ @NeverInline
+ default void foo() {
+ System.out.println("I::foo");
+ }
+ }
+
+ @NoVerticalClassMerging
+ public interface J extends I {
+
+ @Override
+ default void foo() {
+ I.super.foo();
+ }
+ }
+
+ public static class Main implements J {
+
+ public static void main(String[] args) {
+ new Main().callSuper();
+ }
+
+ @NeverInline
+ public void callSuper() {
+ J.super.foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/BridgeWithInvokeSuperTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/BridgeWithInvokeSuperTest.java
new file mode 100644
index 0000000..b965fea
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/BridgeWithInvokeSuperTest.java
@@ -0,0 +1,101 @@
+// 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.bridgeremoval;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+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 BridgeWithInvokeSuperTest extends TestBase {
+
+ private static final String[] EXPECTED = new String[] {"I::foo"};
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(Main.class, I.class)
+ .addProgramClassFileData(getAWithBridgeAccessFlag())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class, I.class)
+ .addProgramClassFileData(getAWithBridgeAccessFlag())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .enableNoVerticalClassMergingAnnotations()
+ .enableInliningAnnotations()
+ .compile()
+ .inspect(
+ inspector -> {
+ // Check that we are removing the bridge if we support default methods.
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ ClassSubject A = inspector.clazz(A.class);
+ assertThat(A, isPresent());
+ MethodSubject fooMethod = A.uniqueMethodWithOriginalName("foo");
+ assertThat(fooMethod, not(isPresent()));
+ }
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ private byte[] getAWithBridgeAccessFlag() throws Exception {
+ return transformer(A.class)
+ .setAccessFlags(MethodPredicate.onName("foo"), MethodAccessFlags::setBridge)
+ .transform();
+ }
+
+ @NoVerticalClassMerging
+ public interface I {
+
+ @NeverInline
+ default void foo() {
+ System.out.println("I::foo");
+ }
+ }
+
+ public static class A implements I {
+
+ @Override
+ @NeverInline
+ public void foo() {
+ I.super.foo();
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new A().foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesTest.java
index cef3785..e6015ad 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesTest.java
@@ -25,9 +25,11 @@
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileTime;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
+import java.time.Instant;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -157,6 +159,7 @@
readWriteThroughFilesAPI(path);
readThroughFileChannelAPI(path);
attributeAccess(path);
+ Files.setAttribute(path, "basic:lastModifiedTime", FileTime.from(Instant.EPOCH));
fspMethodsWithGeneric(path);
pathGeneric();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/InputStreamTransferToTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/InputStreamTransferToTest.java
index 70d445d..130cce2 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/InputStreamTransferToTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/InputStreamTransferToTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
@@ -67,4 +68,23 @@
.run(parameters.getRuntime(), MAIN_CLASS)
.assertSuccessWithOutput(EXPECTED_OUTPUT);
}
+
+ /**
+ * See b/248200357, in T an override or transferTo was introduced in android.jar changing
+ * resolution.
+ */
+ @Test
+ public void testWithAndroidJarFromT() throws Exception {
+ // The method is not present on JDK8 so if we don't desugar that won't work.
+ Assume.assumeFalse(
+ parameters.isCfRuntime(CfVm.JDK8)
+ && !libraryDesugaringSpecification.hasNioFileDesugaring(parameters)
+ && compilationSpecification.isCfToCf());
+ testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+ .addProgramFiles(INPUT_JAR)
+ .addKeepMainRule(MAIN_CLASS)
+ .overrideLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/PathOfTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/PathOfTest.java
new file mode 100644
index 0000000..33fa6ab
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/PathOfTest.java
@@ -0,0 +1,61 @@
+// 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.desugar.desugaredlibrary.jdk11;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+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 PathOfTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+ private final CompilationSpecification compilationSpecification;
+
+ private static final Path INPUT_JAR = Paths.get(ToolHelper.EXAMPLES_JAVA11_JAR_DIR + "path.jar");
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("foo/bar", "bar");
+ private static final String MAIN_CLASS = "path.PathExample";
+
+ @Parameters(name = "{0}, spec: {1}, {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+ ImmutableList.of(JDK11_PATH),
+ DEFAULT_SPECIFICATIONS);
+ }
+
+ public PathOfTest(
+ TestParameters parameters,
+ LibraryDesugaringSpecification libraryDesugaringSpecification,
+ CompilationSpecification compilationSpecification) {
+ this.parameters = parameters;
+ this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+ this.compilationSpecification = compilationSpecification;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+ .addProgramFiles(INPUT_JAR)
+ .addKeepMainRule(MAIN_CLASS)
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
index 2a07cfb..a66e6b4 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
@@ -293,7 +293,8 @@
assertEquals(
humanRewritingFlags1.getEmulatedInterfaces(), humanRewritingFlags2.getEmulatedInterfaces());
assertEquals(
- humanRewritingFlags1.getRetargetMethod(), humanRewritingFlags2.getRetargetMethod());
+ humanRewritingFlags1.getRetargetMethodToType(),
+ humanRewritingFlags2.getRetargetMethodToType());
assertEquals(humanRewritingFlags1.getDontRetarget(), humanRewritingFlags2.getDontRetarget());
assertEquals(
diff --git a/src/test/java/com/android/tools/r8/keepanno/asm/KeepEdgeAsmTest.java b/src/test/java/com/android/tools/r8/keepanno/asm/KeepEdgeAsmTest.java
new file mode 100644
index 0000000..b9ebfa2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/asm/KeepEdgeAsmTest.java
@@ -0,0 +1,84 @@
+// 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.keepanno.asm;
+
+import static org.junit.Assert.assertEquals;
+
+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.ast.KeepEdge;
+import com.android.tools.r8.keepanno.testsource.KeepClassAndDefaultConstructorSource;
+import com.android.tools.r8.keepanno.testsource.KeepSourceEdges;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.transformers.ClassTransformer;
+import java.util.Collections;
+import java.util.Set;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.objectweb.asm.AnnotationVisitor;
+
+@RunWith(Parameterized.class)
+public class KeepEdgeAsmTest extends TestBase {
+
+ private static final Class<?> SOURCE = KeepClassAndDefaultConstructorSource.class;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public KeepEdgeAsmTest(TestParameters parameters) {
+ parameters.assertNoneRuntime();
+ }
+
+ @Test
+ public void testAsmReader() throws Exception {
+ Set<KeepEdge> expectedEdges = KeepSourceEdges.getExpectedEdges(SOURCE);
+ ClassReference clazz = Reference.classFromClass(SOURCE);
+ // Original bytes of the test class.
+ byte[] original = ToolHelper.getClassAsBytes(SOURCE);
+ // Strip out all the annotations to ensure they are actually added again.
+ byte[] stripped =
+ transformer(SOURCE)
+ .addClassTransformer(
+ new ClassTransformer() {
+ @Override
+ public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+ // Ignore all input annotations.
+ return null;
+ }
+ })
+ .transform();
+ // Manually add in the expected edges again.
+ byte[] readded =
+ transformer(stripped, clazz)
+ .addClassTransformer(
+ new ClassTransformer() {
+
+ @Override
+ public void visitEnd() {
+ for (KeepEdge edge : expectedEdges) {
+ KeepEdgeWriter.writeEdge(edge, super::visitAnnotation);
+ }
+ super.visitEnd();
+ }
+ })
+ .transform();
+
+ // Read the edges from each version.
+ Set<KeepEdge> originalEdges = KeepEdgeReader.readKeepEdges(original);
+ Set<KeepEdge> strippedEdges = KeepEdgeReader.readKeepEdges(stripped);
+ Set<KeepEdge> readdedEdges = KeepEdgeReader.readKeepEdges(readded);
+
+ // The edges are compared to the "expected" ast to ensure we don't hide failures in reading or
+ // writing.
+ assertEquals(Collections.emptySet(), strippedEdges);
+ assertEquals(expectedEdges, originalEdges);
+ assertEquals(expectedEdges, readdedEdges);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/experimental/keepanno/ast/KeepEdgeApiTest.java b/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
similarity index 87%
rename from src/test/java/com/android/tools/r8/experimental/keepanno/ast/KeepEdgeApiTest.java
rename to src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
index d69d055..bcdb8ed 100644
--- a/src/test/java/com/android/tools/r8/experimental/keepanno/ast/KeepEdgeApiTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
@@ -1,22 +1,22 @@
// 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.experimental.keepanno.ast;
+package com.android.tools.r8.keepanno.ast;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.experimental.keepanno.ast.KeepOptions.KeepOption;
-import com.android.tools.r8.experimental.keepanno.keeprules.KeepRuleExtractor;
+import com.android.tools.r8.keepanno.ast.KeepOptions.KeepOption;
+import com.android.tools.r8.keepanno.keeprules.KeepRuleExtractor;
import com.android.tools.r8.utils.StringUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
-public class KeepEdgeApiTest extends TestBase {
+public class KeepEdgeAstTest extends TestBase {
private static String CLASS = "com.example.Foo";
@@ -25,7 +25,7 @@
return getTestParameters().withNoneRuntime().build();
}
- public KeepEdgeApiTest(TestParameters parameters) {
+ public KeepEdgeAstTest(TestParameters parameters) {
parameters.assertNoneRuntime();
}
@@ -102,10 +102,7 @@
KeepEdge.builder()
.setPreconditions(
KeepPreconditions.builder()
- .addCondition(
- KeepCondition.builder()
- .setItem(classItem(CLASS))
- .build())
+ .addCondition(KeepCondition.builder().setItem(classItem(CLASS)).build())
.build())
.setConsequences(
KeepConsequences.builder()
@@ -127,10 +124,7 @@
KeepEdge.builder()
.setPreconditions(
KeepPreconditions.builder()
- .addCondition(
- KeepCondition.builder()
- .setItem(classItem(CLASS))
- .build())
+ .addCondition(KeepCondition.builder().setItem(classItem(CLASS)).build())
.build())
.setConsequences(KeepConsequences.builder().addTarget(target(classItem(CLASS))).build())
.build();
@@ -144,10 +138,7 @@
KeepEdge.builder()
.setPreconditions(
KeepPreconditions.builder()
- .addCondition(
- KeepCondition.builder()
- .setItem(classItem(CLASS))
- .build())
+ .addCondition(KeepCondition.builder().setItem(classItem(CLASS)).build())
.build())
.setConsequences(
KeepConsequences.builder()
diff --git a/src/test/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessorTest.java b/src/test/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessorTest.java
new file mode 100644
index 0000000..eb7b616
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessorTest.java
@@ -0,0 +1,101 @@
+// 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.keepanno.processor;
+
+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.JavaCompilerTool;
+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.KeepConstants.Edge;
+import com.android.tools.r8.keepanno.asm.KeepEdgeReader;
+import com.android.tools.r8.keepanno.ast.KeepEdge;
+import com.android.tools.r8.keepanno.testsource.KeepClassAndDefaultConstructorSource;
+import com.android.tools.r8.keepanno.testsource.KeepSourceEdges;
+import com.android.tools.r8.utils.ZipUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.Set;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class KeepEdgeProcessorTest extends TestBase {
+
+ private static final Path KEEP_ANNO_PATH =
+ Paths.get(ToolHelper.BUILD_DIR, "classes", "java", "keepanno");
+ private static final Class<?> SOURCE = KeepClassAndDefaultConstructorSource.class;
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDefaultCfRuntime().build();
+ }
+
+ public KeepEdgeProcessorTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testClassfile() throws Exception {
+ Path out =
+ JavaCompilerTool.create(parameters.getRuntime().asCf(), temp)
+ .addAnnotationProcessors(typeName(KeepEdgeProcessor.class))
+ .addClasspathFiles(KEEP_ANNO_PATH)
+ .addClassNames(Collections.singletonList(typeName(SOURCE)))
+ .addClasspathFiles(Paths.get(ToolHelper.BUILD_DIR, "classes", "java", "test"))
+ .addClasspathFiles(ToolHelper.DEPS)
+ .compile();
+
+ CodeInspector inspector = new CodeInspector(out);
+ checkSynthesizedKeepEdgeClass(inspector, out);
+ // The source is added as a classpath name but not part of the compilation unit output.
+ assertThat(inspector.clazz(SOURCE), isAbsent());
+ }
+
+ @Test
+ public void testJavaSource() throws Exception {
+ Path out =
+ JavaCompilerTool.create(parameters.getRuntime().asCf(), temp)
+ .addSourceFiles(ToolHelper.getSourceFileForTestClass(SOURCE))
+ .addAnnotationProcessors(typeName(KeepEdgeProcessor.class))
+ .addClasspathFiles(KEEP_ANNO_PATH)
+ .addClasspathFiles(ToolHelper.DEPS)
+ .compile();
+
+ testForJvm()
+ .addProgramFiles(out)
+ .run(parameters.getRuntime(), SOURCE)
+ .assertSuccessWithOutputLines("A is alive!")
+ .inspect(
+ inspector -> {
+ assertThat(inspector.clazz(SOURCE), isPresent());
+ checkSynthesizedKeepEdgeClass(inspector, out);
+ });
+ }
+
+ private void checkSynthesizedKeepEdgeClass(CodeInspector inspector, Path data)
+ throws IOException {
+ String synthesizedEdgesClassName =
+ KeepEdgeProcessor.getClassTypeNameForSynthesizedEdges(SOURCE.getTypeName());
+ ClassSubject synthesizedEdgesClass = inspector.clazz(synthesizedEdgesClassName);
+ assertThat(synthesizedEdgesClass, isPresent());
+ assertThat(synthesizedEdgesClass.annotation(Edge.CLASS.getTypeName()), isPresent());
+ String entry = ZipUtils.zipEntryNameForClass(synthesizedEdgesClass.getFinalReference());
+ byte[] bytes = ZipUtils.readSingleEntry(data, entry);
+ Set<KeepEdge> keepEdges = KeepEdgeReader.readKeepEdges(bytes);
+ assertEquals(KeepSourceEdges.getExpectedEdges(SOURCE), keepEdges);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/testsource/KeepClassAndDefaultConstructorSource.java b/src/test/java/com/android/tools/r8/keepanno/testsource/KeepClassAndDefaultConstructorSource.java
new file mode 100644
index 0000000..ca83a46
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/testsource/KeepClassAndDefaultConstructorSource.java
@@ -0,0 +1,32 @@
+// 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.keepanno.testsource;
+
+import com.android.tools.r8.keepanno.annotations.KeepEdge;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+
+@KeepEdge(
+ consequences = {
+ // Keep the class to allow lookup of it.
+ @KeepTarget(classConstant = KeepClassAndDefaultConstructorSource.class),
+ // Keep the default constructor.
+ @KeepTarget(classConstant = KeepClassAndDefaultConstructorSource.class, methodName = "<init>")
+ })
+public class KeepClassAndDefaultConstructorSource {
+
+ public static class A {
+ public A() {
+ System.out.println("A is alive!");
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ Class<?> aClass =
+ Class.forName(
+ KeepClassAndDefaultConstructorSource.class.getPackage().getName()
+ + (args.length > 0 ? ".." : ".")
+ + "KeepClassAndDefaultConstructorSource$A");
+ aClass.getDeclaredConstructor().newInstance();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/testsource/KeepSourceEdges.java b/src/test/java/com/android/tools/r8/keepanno/testsource/KeepSourceEdges.java
new file mode 100644
index 0000000..187a019
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/testsource/KeepSourceEdges.java
@@ -0,0 +1,54 @@
+// 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.keepanno.testsource;
+
+import com.android.tools.r8.keepanno.ast.KeepConsequences;
+import com.android.tools.r8.keepanno.ast.KeepEdge;
+import com.android.tools.r8.keepanno.ast.KeepItemPattern;
+import com.android.tools.r8.keepanno.ast.KeepMembersPattern;
+import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern;
+import com.android.tools.r8.keepanno.ast.KeepMethodPattern;
+import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern;
+import com.android.tools.r8.keepanno.ast.KeepTarget;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Utility to get the AST edges for the various annotation test sources.
+ *
+ * <p>This makes it easier to share the test sources among tests (e.g., processor and asm tests).
+ */
+public class KeepSourceEdges {
+
+ public static Set<KeepEdge> getExpectedEdges(Class<?> clazz) {
+ if (clazz.equals(KeepClassAndDefaultConstructorSource.class)) {
+ return getKeepClassAndDefaultConstructorSourceEdges();
+ }
+ throw new RuntimeException();
+ }
+
+ public static Set<KeepEdge> getKeepClassAndDefaultConstructorSourceEdges() {
+ Class<?> clazz = KeepClassAndDefaultConstructorSource.class;
+ // Build the class target.
+ KeepQualifiedClassNamePattern name = KeepQualifiedClassNamePattern.exact(clazz.getTypeName());
+ KeepItemPattern classItem = KeepItemPattern.builder().setClassPattern(name).build();
+ KeepTarget classTarget = KeepTarget.builder().setItem(classItem).build();
+ // Build the constructor target.
+ KeepMethodPattern constructorMethod =
+ KeepMethodPattern.builder().setNamePattern(KeepMethodNamePattern.exact("<init>")).build();
+ KeepMembersPattern constructorMembers =
+ KeepMembersPattern.builder().addMethodPattern(constructorMethod).build();
+ KeepItemPattern constructorItem =
+ KeepItemPattern.builder()
+ .setClassPattern(name)
+ .setMembersPattern(constructorMembers)
+ .build();
+ KeepTarget constructorTarget = KeepTarget.builder().setItem(constructorItem).build();
+ // The consequet set is the class an its constructor.
+ KeepConsequences consequences =
+ KeepConsequences.builder().addTarget(classTarget).addTarget(constructorTarget).build();
+ KeepEdge edge = KeepEdge.builder().setConsequences(consequences).build();
+ return Collections.singleton(edge);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeTest.java
index d569899..6d96680 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeTest.java
@@ -5,9 +5,8 @@
package com.android.tools.r8.memberrebinding;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isSynthetic;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
@@ -16,7 +15,6 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -58,10 +56,7 @@
inspector -> {
ClassSubject clazz = inspector.clazz(J.class);
assertThat(clazz, isPresent());
- // TODO(b/197490164): We should remove the bridge inserted here.
- assertEquals(1, clazz.allMethods().size());
- FoundMethodSubject foundMethodSubject = clazz.allMethods().get(0);
- assertThat(foundMethodSubject, isSynthetic());
+ assertTrue(clazz.allMethods().isEmpty());
});
}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeWithSubTypeTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeWithSubTypeTest.java
index 4551dbe..dd2f0c1 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeWithSubTypeTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeWithSubTypeTest.java
@@ -5,9 +5,8 @@
package com.android.tools.r8.memberrebinding;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isSynthetic;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
@@ -18,7 +17,6 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -63,10 +61,7 @@
inspector -> {
ClassSubject clazz = inspector.clazz(A.class);
assertThat(clazz, isPresent());
- // TODO(b/197490164): We should remove the bridge inserted here.
- assertEquals(1, clazz.virtualMethods().size());
- FoundMethodSubject foundMethodSubject = clazz.virtualMethods().get(0);
- assertThat(foundMethodSubject, isSynthetic());
+ assertTrue(clazz.virtualMethods().isEmpty());
});
}
diff --git a/src/test/java/com/android/tools/r8/shaking/b136195382/AbstractBridgeInheritTest.java b/src/test/java/com/android/tools/r8/shaking/b136195382/AbstractBridgeInheritTest.java
index f1fc012..58faf90 100644
--- a/src/test/java/com/android/tools/r8/shaking/b136195382/AbstractBridgeInheritTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/b136195382/AbstractBridgeInheritTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.shaking.b136195382;
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -13,8 +12,6 @@
import com.android.tools.r8.shaking.b136195382.package2.Main;
import com.android.tools.r8.shaking.b136195382.package2.SubFactory;
import com.android.tools.r8.shaking.b136195382.package2.SubService;
-import java.io.IOException;
-import java.util.concurrent.ExecutionException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -35,8 +32,7 @@
}
@Test
- public void testRemovingBridge()
- throws ExecutionException, CompilationFailedException, IOException {
+ public void testRemovingBridge() throws Exception {
testForR8(parameters.getBackend())
.addProgramClasses(
Service.class, Factory.class, SubService.class, SubFactory.class, Main.class)
@@ -44,7 +40,6 @@
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines("Hello World!")
- .inspector();
+ .assertSuccessWithOutputLines("Hello World!");
}
}