Merge commit 'a2f77004a2451840c4f5b504f4a4c0126bf16a7a' into dev-release
diff --git a/build.gradle b/build.gradle
index bde2f60..49d3ecf 100644
--- a/build.gradle
+++ b/build.gradle
@@ -33,7 +33,7 @@
ext {
androidSupportVersion = '25.4.0'
- asmVersion = '9.3' // When updating update tools/asmifier.py, build.src and Toolhelper as well.
+ asmVersion = '9.4' // When updating update tools/asmifier.py, build.src and Toolhelper as well.
espressoVersion = '3.0.0'
fastutilVersion = '7.2.0'
guavaVersion = '30.1.1-jre'
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index 81527b4..3f4e21f 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -8,7 +8,7 @@
mavenCentral()
}
ext {
- asmVersion = '9.3'
+ asmVersion = '9.4'
}
dependencies {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java
index 01c493f..2d5eb60 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java
@@ -25,9 +25,13 @@
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface KeepCondition {
+ String className() default "";
+
Class<?> classConstant() default Object.class;
- String classTypeName() default "";
+ String extendsClassName() default "";
+
+ Class<?> extendsClassConstant() default Object.class;
String methodName() default "";
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
index 0018dfc..02eb5df 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstants.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstants.java
@@ -19,9 +19,20 @@
return classTypeName.replace('.', '/');
}
+ public static boolean isKeepAnnotation(String descriptor, boolean visible) {
+ if (visible) {
+ return false;
+ }
+ return descriptor.equals(Edge.DESCRIPTOR)
+ || descriptor.equals(UsesReflection.DESCRIPTOR)
+ || descriptor.equals(Condition.DESCRIPTOR)
+ || descriptor.equals(Target.DESCRIPTOR);
+ }
+
public static final class Edge {
public static final Class<KeepEdge> CLASS = KeepEdge.class;
public static final String DESCRIPTOR = getDescriptor(CLASS);
+ public static final String description = "description";
public static final String preconditions = "preconditions";
public static final String consequences = "consequences";
}
@@ -30,14 +41,19 @@
public static final Class<com.android.tools.r8.keepanno.annotations.UsesReflection> CLASS =
com.android.tools.r8.keepanno.annotations.UsesReflection.class;
public static final String DESCRIPTOR = getDescriptor(CLASS);
+ public static final String description = "description";
public static final String value = "value";
public static final String additionalPreconditions = "additionalPreconditions";
}
// Implicit hidden item which is "super type" of Condition and Target.
public static final class Item {
+ public static final String className = "className";
public static final String classConstant = "classConstant";
+ public static final String extendsClassName = "extendsClassName";
+ public static final String extendsClassConstant = "extendsClassConstant";
+
public static final String methodName = "methodName";
public static final String methodReturnType = "methodReturnType";
public static final String methodParameters = "methodParameters";
@@ -48,6 +64,12 @@
// Default values for the optional entries. The defaults should be chosen such that they do
// not coincide with any actual valid value. E.g., the empty string in place of a name or type.
// These must be 1:1 with the value defined on the actual annotation definition.
+ public static final String classNameDefault = "";
+ public static final Class<?> classConstantDefault = Object.class;
+
+ public static final String extendsClassNameDefault = "";
+ public static final Class<?> extendsClassConstantDefault = Object.class;
+
public static final String methodNameDefaultValue = "";
public static final String methodReturnTypeDefaultValue = "";
public static final String[] methodParametersDefaultValue = new String[] {""};
@@ -64,5 +86,19 @@
public static final class Target {
public static final Class<KeepTarget> CLASS = KeepTarget.class;
public static final String DESCRIPTOR = getDescriptor(CLASS);
+
+ public static final String allow = "allow";
+ public static final String disallow = "disallow";
+ }
+
+ public static final class Option {
+ public static final Class<KeepOption> CLASS = KeepOption.class;
+ public static final String DESCRIPTOR = getDescriptor(CLASS);
+
+ public static final String SHRINKING = "SHRINKING";
+ public static final String OBFUSCATION = "OBFUSCATION";
+ public static final String OPTIMIZATION = "OPTIMIZATION";
+ public static final String ACCESS_MODIFICATION = "ACCESS_MODIFICATION";
+ public static final String ANNOTATION_REMOVAL = "ANNOTATION_REMOVAL";
}
}
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
index fb9a772..329b153 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepEdge.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepEdge.java
@@ -8,9 +8,11 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
+@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.CLASS)
public @interface KeepEdge {
+ String description() default "";
+
KeepCondition[] preconditions() default {};
KeepTarget[] consequences();
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepOption.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepOption.java
new file mode 100644
index 0000000..e3c6d31
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepOption.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.annotations;
+
+public enum KeepOption {
+ SHRINKING,
+ OPTIMIZATION,
+ OBFUSCATION,
+ ACCESS_MODIFICATION,
+ ANNOTATION_REMOVAL,
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java
index 73559d6..e4cb263 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java
@@ -25,9 +25,22 @@
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface KeepTarget {
+
+ // KeepTarget only content (keep options) =========
+
+ KeepOption[] allow() default {};
+
+ KeepOption[] disallow() default {};
+
+ // Shared KeepItem content ========================
+
+ String className() default "";
+
Class<?> classConstant() default Object.class;
- String classTypeName() default "";
+ String extendsClassName() default "";
+
+ Class<?> extendsClassConstant() default Object.class;
String methodName() default "";
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsesReflection.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsesReflection.java
index 22fcb36..0af2ec6 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsesReflection.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsesReflection.java
@@ -53,10 +53,13 @@
* }
* </pre>
*/
-@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
+@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.CLASS)
public @interface UsesReflection {
+ String description() default "";
+
KeepTarget[] value();
KeepCondition[] additionalPreconditions() 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
index 672a99e7..5d23d5d 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
@@ -7,26 +7,32 @@
import com.android.tools.r8.keepanno.annotations.KeepConstants.Condition;
import com.android.tools.r8.keepanno.annotations.KeepConstants.Edge;
import com.android.tools.r8.keepanno.annotations.KeepConstants.Item;
+import com.android.tools.r8.keepanno.annotations.KeepConstants.Option;
import com.android.tools.r8.keepanno.annotations.KeepConstants.Target;
import com.android.tools.r8.keepanno.ast.KeepCondition;
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.KeepEdgeMetaInfo;
+import com.android.tools.r8.keepanno.ast.KeepExtendsPattern;
import com.android.tools.r8.keepanno.ast.KeepFieldNamePattern;
import com.android.tools.r8.keepanno.ast.KeepFieldPattern;
import com.android.tools.r8.keepanno.ast.KeepFieldTypePattern;
import com.android.tools.r8.keepanno.ast.KeepItemPattern;
-import com.android.tools.r8.keepanno.ast.KeepItemPattern.Builder;
+import com.android.tools.r8.keepanno.ast.KeepMemberPattern;
import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern;
import com.android.tools.r8.keepanno.ast.KeepMethodParametersPattern;
import com.android.tools.r8.keepanno.ast.KeepMethodPattern;
import com.android.tools.r8.keepanno.ast.KeepMethodReturnTypePattern;
+import com.android.tools.r8.keepanno.ast.KeepOptions;
+import com.android.tools.r8.keepanno.ast.KeepOptions.KeepOption;
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 java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -77,23 +83,27 @@
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+ // Skip any visible annotations as @KeepEdge is not runtime visible.
if (visible) {
return null;
}
- // Skip any visible annotations as @KeepEdge is not runtime visible.
if (descriptor.equals(Edge.DESCRIPTOR)) {
- return new KeepEdgeVisitor(parent);
+ return new KeepEdgeVisitor(parent, this::setContext);
}
if (descriptor.equals(KeepConstants.UsesReflection.DESCRIPTOR)) {
KeepItemPattern classItem =
KeepItemPattern.builder()
.setClassPattern(KeepQualifiedClassNamePattern.exact(className))
.build();
- return new UsesReflectionVisitor(parent, classItem);
+ return new UsesReflectionVisitor(parent, this::setContext, classItem);
}
return null;
}
+ private void setContext(KeepEdgeMetaInfo.Builder builder) {
+ builder.setContextFromClassDescriptor(KeepEdgeReaderUtils.javaTypeToDescriptor(className));
+ }
+
@Override
public MethodVisitor visitMethod(
int access, String name, String descriptor, String signature, String[] exceptions) {
@@ -152,13 +162,18 @@
return null;
}
if (descriptor.equals(Edge.DESCRIPTOR)) {
- return new KeepEdgeVisitor(parent);
+ return new KeepEdgeVisitor(parent, this::setContext);
}
if (descriptor.equals(KeepConstants.UsesReflection.DESCRIPTOR)) {
- return new UsesReflectionVisitor(parent, createItemContext());
+ return new UsesReflectionVisitor(parent, this::setContext, createItemContext());
}
return null;
}
+
+ private void setContext(KeepEdgeMetaInfo.Builder builder) {
+ builder.setContextFromMethodDescriptor(
+ KeepEdgeReaderUtils.javaTypeToDescriptor(className), methodName, methodDescriptor);
+ }
}
private static class KeepEdgeFieldVisitor extends FieldVisitor {
@@ -189,6 +204,11 @@
.build();
}
+ private void setContext(KeepEdgeMetaInfo.Builder builder) {
+ builder.setContextFromFieldDescriptor(
+ KeepEdgeReaderUtils.javaTypeToDescriptor(className), fieldName, fieldDescriptor);
+ }
+
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
// Skip any visible annotations as @KeepEdge is not runtime visible.
@@ -196,10 +216,10 @@
return null;
}
if (descriptor.equals(Edge.DESCRIPTOR)) {
- return new KeepEdgeVisitor(parent);
+ return new KeepEdgeVisitor(parent, this::setContext);
}
if (descriptor.equals(KeepConstants.UsesReflection.DESCRIPTOR)) {
- return new UsesReflectionVisitor(parent, createItemContext());
+ return new UsesReflectionVisitor(parent, this::setContext, createItemContext());
}
return null;
}
@@ -240,9 +260,20 @@
private static class KeepEdgeVisitor extends AnnotationVisitorBase {
private final Parent<KeepEdge> parent;
private final KeepEdge.Builder builder = KeepEdge.builder();
+ private final KeepEdgeMetaInfo.Builder metaInfoBuilder = KeepEdgeMetaInfo.builder();
- KeepEdgeVisitor(Parent<KeepEdge> parent) {
+ KeepEdgeVisitor(Parent<KeepEdge> parent, Consumer<KeepEdgeMetaInfo.Builder> addContext) {
this.parent = parent;
+ addContext.accept(metaInfoBuilder);
+ }
+
+ @Override
+ public void visit(String name, Object value) {
+ if (name.equals(Edge.description) && value instanceof String) {
+ metaInfoBuilder.setDescription((String) value);
+ return;
+ }
+ super.visit(name, value);
}
@Override
@@ -258,7 +289,7 @@
@Override
public void visitEnd() {
- parent.accept(builder.build());
+ parent.accept(builder.setMetaInfo(metaInfoBuilder.build()).build());
}
}
@@ -266,10 +297,24 @@
private final Parent<KeepEdge> parent;
private final KeepEdge.Builder builder = KeepEdge.builder();
private final KeepPreconditions.Builder preconditions = KeepPreconditions.builder();
+ private final KeepEdgeMetaInfo.Builder metaInfoBuilder = KeepEdgeMetaInfo.builder();
- UsesReflectionVisitor(Parent<KeepEdge> parent, KeepItemPattern context) {
+ UsesReflectionVisitor(
+ Parent<KeepEdge> parent,
+ Consumer<KeepEdgeMetaInfo.Builder> addContext,
+ KeepItemPattern context) {
this.parent = parent;
preconditions.addCondition(KeepCondition.builder().setItem(context).build());
+ addContext.accept(metaInfoBuilder);
+ }
+
+ @Override
+ public void visit(String name, Object value) {
+ if (name.equals(Edge.description) && value instanceof String) {
+ metaInfoBuilder.setDescription((String) value);
+ return;
+ }
+ super.visit(name, value);
}
@Override
@@ -288,7 +333,11 @@
@Override
public void visitEnd() {
- parent.accept(builder.setPreconditions(preconditions.build()).build());
+ parent.accept(
+ builder
+ .setMetaInfo(metaInfoBuilder.build())
+ .setPreconditions(preconditions.build())
+ .build());
}
}
@@ -325,7 +374,7 @@
@Override
public AnnotationVisitor visitAnnotation(String name, String descriptor) {
if (descriptor.equals(Target.DESCRIPTOR)) {
- return new KeepTargetVisitor(builder::addTarget);
+ return KeepTargetVisitor.create(builder::addTarget);
}
return super.visitAnnotation(name, descriptor);
}
@@ -336,80 +385,176 @@
}
}
- private abstract static class KeepItemVisitorBase extends AnnotationVisitorBase {
- private final Parent<KeepItemPattern> parent;
+ private abstract static class Declaration<T> {
+ abstract String kind();
- private KeepQualifiedClassNamePattern classNamePattern = null;
- private KeepMethodPattern.Builder lazyMethodBuilder = null;
- private KeepFieldPattern.Builder lazyFieldBuilder = null;
+ abstract T getValue();
- public KeepItemVisitorBase(Parent<KeepItemPattern> parent) {
- this.parent = parent;
+ boolean tryParse(String name, Object value) {
+ return false;
}
- private KeepMethodPattern.Builder methodBuilder() {
- if (lazyFieldBuilder != null) {
- throw new KeepEdgeException("Cannot define both a field and a method pattern");
- }
- if (lazyMethodBuilder == null) {
- lazyMethodBuilder = KeepMethodPattern.builder();
- }
- return lazyMethodBuilder;
+ AnnotationVisitor tryParseArray(String name, Consumer<T> onValue) {
+ return null;
+ }
+ }
+
+ private abstract static class SingleDeclaration<T> extends Declaration<T> {
+ private String declarationName = null;
+ private T declarationValue = null;
+ private AnnotationVisitor declarationVisitor = null;
+
+ abstract T getDefaultValue();
+
+ abstract T parse(String name, Object value);
+
+ AnnotationVisitor parseArray(String name, Consumer<T> setValue) {
+ return null;
}
- private KeepFieldPattern.Builder fieldBuilder() {
- if (lazyMethodBuilder != null) {
- throw new KeepEdgeException("Cannot define both a field and a method pattern");
- }
- if (lazyFieldBuilder == null) {
- lazyFieldBuilder = KeepFieldPattern.builder();
- }
- return lazyFieldBuilder;
+ private boolean hasDeclaration() {
+ return declarationValue != null || declarationVisitor != null;
+ }
+
+ private void error(String name) {
+ throw new KeepEdgeException(
+ "Multiple declarations defining "
+ + kind()
+ + ": '"
+ + declarationName
+ + "' and '"
+ + name
+ + "'");
}
@Override
- public void visit(String name, Object value) {
- if (name.equals(Item.classConstant) && value instanceof Type) {
- classNamePattern = KeepQualifiedClassNamePattern.exact(((Type) value).getClassName());
- return;
+ public final T getValue() {
+ return declarationValue == null ? getDefaultValue() : declarationValue;
+ }
+
+ @Override
+ final boolean tryParse(String name, Object value) {
+ T result = parse(name, value);
+ if (result != null) {
+ if (hasDeclaration()) {
+ error(name);
+ }
+ declarationName = name;
+ declarationValue = result;
+ return true;
}
+ return false;
+ }
+
+ @Override
+ AnnotationVisitor tryParseArray(String name, Consumer<T> setValue) {
+ AnnotationVisitor visitor = parseArray(name, setValue.andThen(v -> declarationValue = v));
+ if (visitor != null) {
+ if (hasDeclaration()) {
+ error(name);
+ }
+ declarationName = name;
+ declarationVisitor = visitor;
+ return visitor;
+ }
+ return null;
+ }
+ }
+
+ private static class ClassDeclaration extends SingleDeclaration<KeepQualifiedClassNamePattern> {
+ @Override
+ String kind() {
+ return "class";
+ }
+
+ @Override
+ KeepQualifiedClassNamePattern getDefaultValue() {
+ return KeepQualifiedClassNamePattern.any();
+ }
+
+ @Override
+ KeepQualifiedClassNamePattern parse(String name, Object value) {
+ if (name.equals(Item.classConstant) && value instanceof Type) {
+ return KeepQualifiedClassNamePattern.exact(((Type) value).getClassName());
+ }
+ if (name.equals(Item.className) && value instanceof String) {
+ return KeepQualifiedClassNamePattern.exact(((String) value));
+ }
+ return null;
+ }
+ }
+
+ private static class ExtendsDeclaration extends SingleDeclaration<KeepExtendsPattern> {
+
+ @Override
+ String kind() {
+ return "extends";
+ }
+
+ @Override
+ KeepExtendsPattern getDefaultValue() {
+ return KeepExtendsPattern.any();
+ }
+
+ @Override
+ KeepExtendsPattern parse(String name, Object value) {
+ if (name.equals(Item.extendsClassConstant) && value instanceof Type) {
+ return KeepExtendsPattern.builder()
+ .classPattern(KeepQualifiedClassNamePattern.exact(((Type) value).getClassName()))
+ .build();
+ }
+ if (name.equals(Item.extendsClassName) && value instanceof String) {
+ return KeepExtendsPattern.builder()
+ .classPattern(KeepQualifiedClassNamePattern.exact(((String) value)))
+ .build();
+ }
+ return null;
+ }
+ }
+
+ private static class MethodDeclaration extends Declaration<KeepMethodPattern> {
+
+ private KeepMethodPattern.Builder builder = null;
+
+ private KeepMethodPattern.Builder getBuilder() {
+ if (builder == null) {
+ builder = KeepMethodPattern.builder();
+ }
+ return builder;
+ }
+
+ @Override
+ String kind() {
+ return "method";
+ }
+
+ @Override
+ KeepMethodPattern getValue() {
+ return builder != null ? builder.build() : null;
+ }
+
+ @Override
+ boolean tryParse(String name, Object value) {
if (name.equals(Item.methodName) && value instanceof String) {
String methodName = (String) value;
if (!Item.methodNameDefaultValue.equals(methodName)) {
- methodBuilder().setNamePattern(KeepMethodNamePattern.exact(methodName));
+ getBuilder().setNamePattern(KeepMethodNamePattern.exact(methodName));
}
- return;
+ return true;
}
if (name.equals(Item.methodReturnType) && value instanceof String) {
String returnType = (String) value;
if (!Item.methodReturnTypeDefaultValue.equals(returnType)) {
- methodBuilder()
+ getBuilder()
.setReturnTypePattern(KeepEdgeReaderUtils.methodReturnTypeFromString(returnType));
}
- return;
+ return true;
}
- if (name.equals(Item.fieldName) && value instanceof String) {
- String fieldName = (String) value;
- if (!Item.fieldNameDefaultValue.equals(fieldName)) {
- fieldBuilder().setNamePattern(KeepFieldNamePattern.exact(fieldName));
- }
- return;
- }
- if (name.equals(Item.fieldType) && value instanceof String) {
- String fieldType = (String) value;
- if (!Item.fieldTypeDefaultValue.equals(fieldType)) {
- fieldBuilder()
- .setTypePattern(
- KeepFieldTypePattern.fromType(
- KeepEdgeReaderUtils.typePatternFromString(fieldType)));
- }
- return;
- }
- super.visit(name, value);
+ return false;
}
@Override
- public AnnotationVisitor visitArray(String name) {
+ AnnotationVisitor tryParseArray(String name, Consumer<KeepMethodPattern> ignored) {
if (name.equals(Item.methodParameters)) {
return new StringArrayVisitor(
params -> {
@@ -420,26 +565,136 @@
for (String param : params) {
builder.addParameterTypePattern(KeepEdgeReaderUtils.typePatternFromString(param));
}
- methodBuilder().setParametersPattern(builder.build());
+ KeepMethodParametersPattern result = builder.build();
+ getBuilder().setParametersPattern(result);
});
}
+ return null;
+ }
+ }
+
+ private static class FieldDeclaration extends Declaration<KeepFieldPattern> {
+
+ private KeepFieldPattern.Builder builder = null;
+
+ private KeepFieldPattern.Builder getBuilder() {
+ if (builder == null) {
+ builder = KeepFieldPattern.builder();
+ }
+ return builder;
+ }
+
+ @Override
+ String kind() {
+ return "field";
+ }
+
+ @Override
+ KeepFieldPattern getValue() {
+ return builder != null ? builder.build() : null;
+ }
+
+ @Override
+ boolean tryParse(String name, Object value) {
+ if (name.equals(Item.fieldName) && value instanceof String) {
+ String fieldName = (String) value;
+ if (!Item.fieldNameDefaultValue.equals(fieldName)) {
+ getBuilder().setNamePattern(KeepFieldNamePattern.exact(fieldName));
+ }
+ return true;
+ }
+ if (name.equals(Item.fieldType) && value instanceof String) {
+ String fieldType = (String) value;
+ if (!Item.fieldTypeDefaultValue.equals(fieldType)) {
+ getBuilder()
+ .setTypePattern(
+ KeepFieldTypePattern.fromType(
+ KeepEdgeReaderUtils.typePatternFromString(fieldType)));
+ }
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private static class MemberDeclaration extends Declaration<KeepMemberPattern> {
+
+ private MethodDeclaration methodDeclaration = new MethodDeclaration();
+ private FieldDeclaration fieldDeclaration = new FieldDeclaration();
+
+ @Override
+ String kind() {
+ return "member";
+ }
+
+ @Override
+ public KeepMemberPattern getValue() {
+ KeepMethodPattern method = methodDeclaration.getValue();
+ KeepFieldPattern field = fieldDeclaration.getValue();
+ if (method != null && field != null) {
+ throw new KeepEdgeException("Cannot define both a field and a method pattern");
+ }
+ if (method != null) {
+ return method;
+ }
+ if (field != null) {
+ return field;
+ }
+ return KeepMemberPattern.none();
+ }
+
+ @Override
+ boolean tryParse(String name, Object value) {
+ return methodDeclaration.tryParse(name, value) || fieldDeclaration.tryParse(name, value);
+ }
+
+ @Override
+ AnnotationVisitor tryParseArray(String name, Consumer<KeepMemberPattern> ignored) {
+ AnnotationVisitor visitor = methodDeclaration.tryParseArray(name, v -> {});
+ if (visitor != null) {
+ return visitor;
+ }
+ return fieldDeclaration.tryParseArray(name, v -> {});
+ }
+ }
+
+ private abstract static class KeepItemVisitorBase extends AnnotationVisitorBase {
+ private final Parent<KeepItemPattern> parent;
+ private final ClassDeclaration classDeclaration = new ClassDeclaration();
+ private final ExtendsDeclaration extendsDeclaration = new ExtendsDeclaration();
+ private final MemberDeclaration memberDeclaration = new MemberDeclaration();
+
+ public KeepItemVisitorBase(Parent<KeepItemPattern> parent) {
+ this.parent = parent;
+ }
+
+ @Override
+ public void visit(String name, Object value) {
+ if (classDeclaration.tryParse(name, value)
+ || extendsDeclaration.tryParse(name, value)
+ || memberDeclaration.tryParse(name, value)) {
+ return;
+ }
+ super.visit(name, value);
+ }
+
+ @Override
+ public AnnotationVisitor visitArray(String name) {
+ AnnotationVisitor visitor = memberDeclaration.tryParseArray(name, v -> {});
+ if (visitor != null) {
+ return visitor;
+ }
return super.visitArray(name);
}
@Override
public void visitEnd() {
- assert lazyMethodBuilder == null || lazyFieldBuilder == null;
- Builder itemBuilder = KeepItemPattern.builder();
- if (classNamePattern != null) {
- itemBuilder.setClassPattern(classNamePattern);
- }
- if (lazyMethodBuilder != null) {
- itemBuilder.setMemberPattern(lazyMethodBuilder.build());
- }
- if (lazyFieldBuilder != null) {
- itemBuilder.setMemberPattern(lazyFieldBuilder.build());
- }
- parent.accept(itemBuilder.build());
+ parent.accept(
+ KeepItemPattern.builder()
+ .setClassPattern(classDeclaration.getValue())
+ .setExtendsPattern(extendsDeclaration.getValue())
+ .setMemberPattern(memberDeclaration.getValue())
+ .build());
}
}
@@ -468,10 +723,59 @@
}
}
+ private static class OptionsDeclaration extends SingleDeclaration<KeepOptions> {
+
+ @Override
+ String kind() {
+ return "options";
+ }
+
+ @Override
+ KeepOptions getDefaultValue() {
+ return KeepOptions.keepAll();
+ }
+
+ @Override
+ KeepOptions parse(String name, Object value) {
+ return null;
+ }
+
+ @Override
+ AnnotationVisitor parseArray(String name, Consumer<KeepOptions> setValue) {
+ if (name.equals(KeepConstants.Target.disallow)) {
+ return new KeepOptionsVisitor(
+ options -> setValue.accept(KeepOptions.disallowBuilder().addAll(options).build()));
+ }
+ if (name.equals(KeepConstants.Target.allow)) {
+ return new KeepOptionsVisitor(
+ options -> setValue.accept(KeepOptions.allowBuilder().addAll(options).build()));
+ }
+ return null;
+ }
+ }
+
private static class KeepTargetVisitor extends KeepItemVisitorBase {
- public KeepTargetVisitor(Parent<KeepTarget> parent) {
- super(item -> parent.accept(KeepTarget.builder().setItem(item).build()));
+ private final KeepTarget.Builder builder;
+ private final OptionsDeclaration optionsDeclaration = new OptionsDeclaration();
+
+ static KeepTargetVisitor create(Parent<KeepTarget> parent) {
+ KeepTarget.Builder builder = KeepTarget.builder();
+ return new KeepTargetVisitor(parent, builder);
+ }
+
+ private KeepTargetVisitor(Parent<KeepTarget> parent, KeepTarget.Builder builder) {
+ super(item -> parent.accept(builder.setItem(item).build()));
+ this.builder = builder;
+ }
+
+ @Override
+ public AnnotationVisitor visitArray(String name) {
+ AnnotationVisitor visitor = optionsDeclaration.tryParseArray(name, builder::setOptions);
+ if (visitor != null) {
+ return visitor;
+ }
+ return super.visitArray(name);
}
}
@@ -481,4 +785,49 @@
super(item -> parent.accept(KeepCondition.builder().setItem(item).build()));
}
}
+
+ private static class KeepOptionsVisitor extends AnnotationVisitorBase {
+
+ private final Parent<Collection<KeepOption>> parent;
+ private final Set<KeepOption> options = new HashSet<>();
+
+ public KeepOptionsVisitor(Parent<Collection<KeepOption>> parent) {
+ this.parent = parent;
+ }
+
+ @Override
+ public void visitEnum(String ignore, String descriptor, String value) {
+ if (!descriptor.equals(KeepConstants.Option.DESCRIPTOR)) {
+ super.visitEnum(ignore, descriptor, value);
+ }
+ KeepOption option;
+ switch (value) {
+ case Option.SHRINKING:
+ option = KeepOption.SHRINKING;
+ break;
+ case Option.OPTIMIZATION:
+ option = KeepOption.OPTIMIZING;
+ break;
+ case Option.OBFUSCATION:
+ option = KeepOption.OBFUSCATING;
+ break;
+ case Option.ACCESS_MODIFICATION:
+ option = KeepOption.ACCESS_MODIFICATION;
+ break;
+ case Option.ANNOTATION_REMOVAL:
+ option = KeepOption.ANNOTATION_REMOVAL;
+ break;
+ default:
+ super.visitEnum(ignore, descriptor, value);
+ return;
+ }
+ options.add(option);
+ }
+
+ @Override
+ public void visitEnd() {
+ parent.accept(options);
+ super.visitEnd();
+ }
+ }
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
index e1d6d4e..dbe52b0 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
@@ -19,7 +19,10 @@
* <p>TODO(b/248408342): Update the BNF and AST to be complete.
*
* <pre>
- * EDGE ::= PRECONDITIONS -> CONSEQUENCES
+ * EDGE ::= METAINFO PRECONDITIONS -> CONSEQUENCES
+ * METAINFO = [CONTEXT] [DESCRIPTION]
+ * CONTEXT = class-descriptor | method-descriptor | field-descriptor
+ * DESCRIPTION = string-content
*
* PRECONDITIONS ::= always | CONDITION+
* CONDITION ::= ITEM_PATTERN
@@ -27,7 +30,7 @@
* CONSEQUENCES ::= TARGET+
* TARGET ::= OPTIONS ITEM_PATTERN
* OPTIONS ::= keep-all | OPTION+
- * OPTION ::= shrinking | optimizing | obfuscating | access-modifying
+ * OPTION ::= shrinking | optimizing | obfuscating | access-modification | annotation-removal
*
* ITEM_PATTERN
* ::= any
@@ -61,6 +64,7 @@
public final class KeepEdge {
public static class Builder {
+ private KeepEdgeMetaInfo metaInfo = KeepEdgeMetaInfo.none();
private KeepPreconditions preconditions = KeepPreconditions.always();
private KeepConsequences consequences;
@@ -76,11 +80,16 @@
return this;
}
+ public Builder setMetaInfo(KeepEdgeMetaInfo metaInfo) {
+ this.metaInfo = metaInfo;
+ return this;
+ }
+
public KeepEdge build() {
if (consequences.isEmpty()) {
throw new KeepEdgeException("KeepEdge must have non-empty set of consequences.");
}
- return new KeepEdge(preconditions, consequences);
+ return new KeepEdge(preconditions, consequences, metaInfo);
}
}
@@ -88,14 +97,22 @@
return new Builder();
}
+ private final KeepEdgeMetaInfo metaInfo;
private final KeepPreconditions preconditions;
private final KeepConsequences consequences;
- private KeepEdge(KeepPreconditions preconditions, KeepConsequences consequences) {
+ private KeepEdge(
+ KeepPreconditions preconditions, KeepConsequences consequences, KeepEdgeMetaInfo metaInfo) {
assert preconditions != null;
assert consequences != null;
+ assert metaInfo != null;
this.preconditions = preconditions;
this.consequences = consequences;
+ this.metaInfo = metaInfo;
+ }
+
+ public KeepEdgeMetaInfo getMetaInfo() {
+ return metaInfo;
}
public KeepPreconditions getPreconditions() {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdgeMetaInfo.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdgeMetaInfo.java
new file mode 100644
index 0000000..662ad4c
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdgeMetaInfo.java
@@ -0,0 +1,247 @@
+// 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;
+
+public class KeepEdgeMetaInfo {
+
+ private static final KeepEdgeMetaInfo NONE =
+ new KeepEdgeMetaInfo(KeepEdgeContext.none(), KeepEdgeDescription.empty());
+
+ public static KeepEdgeMetaInfo none() {
+ return NONE;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ private final KeepEdgeContext context;
+ private final KeepEdgeDescription description;
+
+ private KeepEdgeMetaInfo(KeepEdgeContext context, KeepEdgeDescription description) {
+ this.context = context;
+ this.description = description;
+ }
+
+ public boolean hasDescription() {
+ return !KeepEdgeDescription.empty().equals(description);
+ }
+
+ public String getDescriptionString() {
+ return description.description;
+ }
+
+ public String getContextDescriptorString() {
+ return context.getDescriptorString();
+ }
+
+ public boolean hasContext() {
+ return !KeepEdgeContext.none().equals(context);
+ }
+
+ public static class Builder {
+ private KeepEdgeContext context = KeepEdgeContext.none();
+ private KeepEdgeDescription description = KeepEdgeDescription.empty();
+
+ public Builder setDescription(String description) {
+ this.description = new KeepEdgeDescription(description);
+ return this;
+ }
+
+ public Builder setContextFromClassDescriptor(String classDescriptor) {
+ context = new KeepEdgeClassContext(classDescriptor);
+ return this;
+ }
+
+ public Builder setContextFromMethodDescriptor(
+ String classDescriptor, String methodName, String methodDescriptor) {
+ context = new KeepEdgeMethodContext(classDescriptor, methodName, methodDescriptor);
+ return this;
+ }
+
+ public Builder setContextFromFieldDescriptor(
+ String classDescriptor, String fieldName, String fieldType) {
+ context = new KeepEdgeFieldContext(classDescriptor, fieldName, fieldType);
+ return this;
+ }
+
+ public KeepEdgeMetaInfo build() {
+ if (context.equals(KeepEdgeContext.none())
+ && description.equals(KeepEdgeDescription.empty())) {
+ return none();
+ }
+ return new KeepEdgeMetaInfo(context, description);
+ }
+ }
+
+ private static class KeepEdgeContext {
+ private static final KeepEdgeContext NONE = new KeepEdgeContext();
+
+ public static KeepEdgeContext none() {
+ return NONE;
+ }
+
+ private KeepEdgeContext() {}
+
+ public String getDescriptorString() {
+ throw new KeepEdgeException("Invalid attempt to get descriptor string from none context");
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return this == obj;
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(this);
+ }
+ }
+
+ private static class KeepEdgeClassContext extends KeepEdgeContext {
+ private final String classDescriptor;
+
+ public KeepEdgeClassContext(String classDescriptor) {
+ assert classDescriptor != null;
+ this.classDescriptor = classDescriptor;
+ }
+
+ @Override
+ public String getDescriptorString() {
+ return classDescriptor;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ KeepEdgeClassContext that = (KeepEdgeClassContext) o;
+ return classDescriptor.equals(that.classDescriptor);
+ }
+
+ @Override
+ public int hashCode() {
+ return classDescriptor.hashCode();
+ }
+ }
+
+ private static class KeepEdgeMethodContext extends KeepEdgeContext {
+ private final String classDescriptor;
+ private final String methodName;
+ private final String methodDescriptor;
+
+ public KeepEdgeMethodContext(
+ String classDescriptor, String methodName, String methodDescriptor) {
+ assert classDescriptor != null;
+ assert methodName != null;
+ assert methodDescriptor != null;
+ this.classDescriptor = classDescriptor;
+ this.methodName = methodName;
+ this.methodDescriptor = methodDescriptor;
+ }
+
+ @Override
+ public String getDescriptorString() {
+ return classDescriptor + methodName + methodDescriptor;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ KeepEdgeMethodContext that = (KeepEdgeMethodContext) o;
+ return classDescriptor.equals(that.classDescriptor)
+ && methodName.equals(that.methodName)
+ && methodDescriptor.equals(that.methodDescriptor);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(classDescriptor, methodName, methodDescriptor);
+ }
+ }
+
+ private static class KeepEdgeFieldContext extends KeepEdgeContext {
+ private final String classDescriptor;
+ private final String fieldName;
+ private final String fieldType;
+
+ public KeepEdgeFieldContext(String classDescriptor, String fieldName, String fieldType) {
+ this.classDescriptor = classDescriptor;
+ this.fieldName = fieldName;
+ this.fieldType = fieldType;
+ }
+
+ @Override
+ public String getDescriptorString() {
+ return classDescriptor + fieldName + ":" + fieldType;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ KeepEdgeFieldContext that = (KeepEdgeFieldContext) o;
+ return classDescriptor.equals(that.classDescriptor)
+ && fieldName.equals(that.fieldName)
+ && fieldType.equals(that.fieldType);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(classDescriptor, fieldName, fieldType);
+ }
+ }
+
+ private static class KeepEdgeDescription {
+ private static final KeepEdgeDescription EMPTY = new KeepEdgeDescription("");
+
+ public static KeepEdgeDescription empty() {
+ return EMPTY;
+ }
+
+ private final String description;
+
+ public KeepEdgeDescription(String description) {
+ assert description != null;
+ this.description = description;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ KeepEdgeDescription that = (KeepEdgeDescription) o;
+ return description.equals(that.description);
+ }
+
+ @Override
+ public int hashCode() {
+ return description.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return description;
+ }
+ }
+}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepExtendsPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepExtendsPattern.java
index 02c8768..ea30422 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepExtendsPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepExtendsPattern.java
@@ -7,57 +7,34 @@
public abstract class KeepExtendsPattern {
public static KeepExtendsPattern any() {
- return Any.getInstance();
+ return Some.getAnyInstance();
}
public static class Builder {
- private KeepExtendsPattern pattern;
+ private KeepExtendsPattern pattern = KeepExtendsPattern.any();
private Builder() {}
- public Builder any() {
- pattern = Any.getInstance();
- return this;
- }
-
public Builder classPattern(KeepQualifiedClassNamePattern pattern) {
this.pattern = new Some(pattern);
return this;
}
- }
- private static class Any extends KeepExtendsPattern {
-
- private static final Any INSTANCE = new Any();
-
- public static Any getInstance() {
- return INSTANCE;
- }
-
- @Override
- public boolean isAny() {
- return true;
- }
-
- @Override
- public boolean equals(Object obj) {
- return this == obj;
- }
-
- @Override
- public int hashCode() {
- return System.identityHashCode(this);
- }
-
- @Override
- public String toString() {
- return "*";
+ public KeepExtendsPattern build() {
+ return pattern;
}
}
private static class Some extends KeepExtendsPattern {
+ private static final KeepExtendsPattern ANY_INSTANCE =
+ new Some(KeepQualifiedClassNamePattern.any());
+
+ private static KeepExtendsPattern getAnyInstance() {
+ return ANY_INSTANCE;
+ }
+
private final KeepQualifiedClassNamePattern pattern;
public Some(KeepQualifiedClassNamePattern pattern) {
@@ -71,6 +48,11 @@
}
@Override
+ public KeepQualifiedClassNamePattern asClassNamePattern() {
+ return pattern;
+ }
+
+ @Override
public boolean equals(Object o) {
if (this == o) {
return true;
@@ -100,4 +82,6 @@
private KeepExtendsPattern() {}
public abstract boolean isAny();
+
+ public abstract KeepQualifiedClassNamePattern asClassNamePattern();
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepItemPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepItemPattern.java
index 50d3921..7f84090 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepItemPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepItemPattern.java
@@ -29,7 +29,7 @@
public static class Builder {
- private KeepQualifiedClassNamePattern classNamePattern;
+ private KeepQualifiedClassNamePattern classNamePattern = KeepQualifiedClassNamePattern.any();
private KeepExtendsPattern extendsPattern = KeepExtendsPattern.any();
private KeepMemberPattern memberPattern = KeepMemberPattern.none();
@@ -58,9 +58,6 @@
}
public KeepItemPattern build() {
- if (classNamePattern == null) {
- throw new KeepEdgeException("Class pattern must define a class name pattern.");
- }
return new KeepItemPattern(classNamePattern, extendsPattern, memberPattern);
}
}
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
index 8e1b2e9..ba647eb 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepOptions.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepOptions.java
@@ -22,7 +22,8 @@
SHRINKING,
OPTIMIZING,
OBFUSCATING,
- ACCESS_MODIFYING,
+ ACCESS_MODIFICATION,
+ ANNOTATION_REMOVAL,
}
public static KeepOptions keepAll() {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
index 286c07a..f5739e1 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/keeprules/KeepRuleExtractor.java
@@ -6,6 +6,8 @@
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.KeepEdgeMetaInfo;
+import com.android.tools.r8.keepanno.ast.KeepExtendsPattern;
import com.android.tools.r8.keepanno.ast.KeepFieldAccessPattern;
import com.android.tools.r8.keepanno.ast.KeepFieldNamePattern;
import com.android.tools.r8.keepanno.ast.KeepFieldPattern;
@@ -40,7 +42,7 @@
public void extract(KeepEdge edge) {
List<ItemRule> consequentRules = getConsequentRules(edge.getConsequences());
- printConditionalRules(consequentRules, edge.getPreconditions());
+ printConditionalRules(consequentRules, edge.getPreconditions(), edge.getMetaInfo());
}
private List<ItemRule> getConsequentRules(KeepConsequences consequences) {
@@ -49,8 +51,49 @@
return consequentItems;
}
+ private void printHeader(StringBuilder builder, KeepEdgeMetaInfo metaInfo) {
+ if (metaInfo.hasContext()) {
+ builder.append("# context: ").append(metaInfo.getContextDescriptorString()).append('\n');
+ }
+ if (metaInfo.hasDescription()) {
+ String escapedDescription = escapeLineBreaks(metaInfo.getDescriptionString());
+ builder.append("# description: ").append(escapedDescription).append('\n');
+ }
+ }
+
+ private String escapeChar(char c) {
+ if (c == '\n') {
+ return "\\n";
+ }
+ if (c == '\r') {
+ return "\\r";
+ }
+ return null;
+ }
+
+ private String escapeLineBreaks(String string) {
+ char[] charArray = string.toCharArray();
+ for (int i = 0; i < charArray.length; i++) {
+ // We don't expect escape chars, so wait with constructing a new string until found.
+ if (escapeChar(charArray[i]) != null) {
+ StringBuilder builder = new StringBuilder(string.substring(0, i));
+ for (int j = i; j < charArray.length; j++) {
+ char c = charArray[j];
+ String escaped = escapeChar(c);
+ if (escaped != null) {
+ builder.append(escaped);
+ } else {
+ builder.append(c);
+ }
+ }
+ return builder.toString();
+ }
+ }
+ return string;
+ }
+
private void printConditionalRules(
- List<ItemRule> consequentRules, KeepPreconditions preconditions) {
+ List<ItemRule> consequentRules, KeepPreconditions preconditions, KeepEdgeMetaInfo metaInfo) {
boolean[] hasAtLeastOneConditionalClause = new boolean[1];
preconditions.forEach(
condition -> {
@@ -66,6 +109,7 @@
// disjunctions so conservatively we keep the consequences if any one of
// the preconditions hold.
StringBuilder builder = new StringBuilder();
+ printHeader(builder, metaInfo);
if (!consequentItem.isMemberOnlyConsequent()
|| !conditionItem
.getClassNamePattern()
@@ -82,7 +126,11 @@
if (!hasAtLeastOneConditionalClause[0]) {
// If there are no preconditions, print each consequent as is.
consequentRules.forEach(
- r -> ruleConsumer.accept(printConsequentRule(new StringBuilder(), r).toString()));
+ r -> {
+ StringBuilder builder = new StringBuilder();
+ printHeader(builder, metaInfo);
+ ruleConsumer.accept(printConsequentRule(builder, r).toString());
+ });
}
}
@@ -103,8 +151,10 @@
private static StringBuilder printItem(StringBuilder builder, KeepItemPattern clazzPattern) {
builder.append("class ");
printClassName(builder, clazzPattern.getClassNamePattern());
- if (!clazzPattern.getExtendsPattern().isAny()) {
- throw new Unimplemented();
+ KeepExtendsPattern extendsPattern = clazzPattern.getExtendsPattern();
+ if (!extendsPattern.isAny()) {
+ builder.append(" extends ");
+ printClassName(builder, extendsPattern.asClassNamePattern());
}
KeepMemberPattern member = clazzPattern.getMemberPattern();
if (member.isNone()) {
@@ -247,8 +297,10 @@
return "optimization";
case OBFUSCATING:
return "obfuscation";
- case ACCESS_MODIFYING:
+ case ACCESS_MODIFICATION:
return "accessmodification";
+ case ANNOTATION_REMOVAL:
+ return "annotationremoval";
default:
throw new Unimplemented();
}
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index f578416..aea1c45 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -34,6 +34,7 @@
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.naming.PrefixRewritingNamingLens;
import com.android.tools.r8.naming.RecordRewritingNamingLens;
+import com.android.tools.r8.naming.VarHandleDesugaringRewritingNamingLens;
import com.android.tools.r8.naming.signature.GenericSignatureRewriter;
import com.android.tools.r8.origin.CommandLineOrigin;
import com.android.tools.r8.origin.Origin;
@@ -293,6 +294,13 @@
appView.setNamingLens(
RecordRewritingNamingLens.createRecordRewritingNamingLens(appView)));
+ timing.time(
+ "Create MethodHandle.Lookup rewriting lens",
+ () ->
+ appView.setNamingLens(
+ VarHandleDesugaringRewritingNamingLens
+ .createVarHandleDesugaringRewritingNamingLens(appView)));
+
if (options.isGeneratingDex()
&& hasDexResources
&& hasClassResources
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 5a791ab..3102f0d 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -7,8 +7,6 @@
import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
import com.android.tools.r8.androidapi.ApiReferenceStubber;
-import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.cf.code.CfPosition;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryKeepRuleGenerator;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.ApplicationWriter;
@@ -20,12 +18,9 @@
import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.AppliedGraphLens;
-import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexCode;
-import com.android.tools.r8.graph.DexDebugEvent;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
@@ -36,6 +31,7 @@
import com.android.tools.r8.graph.GenericSignatureCorrectnessHelper;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.graph.analysis.ClassInitializerAssertionEnablingAnalysis;
@@ -947,10 +943,8 @@
if (code == null) {
return;
}
- if (code.isCfCode()) {
- assert verifyOriginalMethodInPosition(code.asCfCode(), originalMethod);
- } else if (code.isDexCode()) {
- assert verifyOriginalMethodInDebugInfo(code.asDexCode(), originalMethod);
+ if (code.isCfCode() || code.isDexCode()) {
+ assert verifyOriginalMethodInPosition(code, originalMethod, method);
} else {
assert code.isDefaultInstanceInitializerCode() || code.isThrowNullCode();
}
@@ -959,26 +953,25 @@
return true;
}
- private static boolean verifyOriginalMethodInPosition(CfCode code, DexMethod originalMethod) {
- for (CfInstruction instruction : code.getInstructions()) {
- if (!instruction.isPosition()) {
- continue;
- }
- CfPosition position = instruction.asPosition();
- assert position.getPosition().getOutermostCaller().getMethod() == originalMethod;
- }
- return true;
- }
-
- private static boolean verifyOriginalMethodInDebugInfo(DexCode code, DexMethod originalMethod) {
- if (code.getDebugInfo() == null || code.getDebugInfo().isPcBasedInfo()) {
- return true;
- }
- for (DexDebugEvent event : code.getDebugInfo().asEventBasedInfo().events) {
- assert !event.isPositionFrame()
- || event.asSetPositionFrame().getPosition().getOutermostCaller().getMethod()
- == originalMethod;
- }
+ private static boolean verifyOriginalMethodInPosition(
+ Code code, DexMethod originalMethod, ProgramMethod context) {
+ code.forEachPosition(
+ position -> {
+ if (position.isOutlineCaller()) {
+ // Check the outlined positions for the original method
+ position
+ .getOutlinePositions()
+ .forEach(
+ (ignored, outlinePosition) -> {
+ assert outlinePosition.hasMethodInChain(originalMethod);
+ });
+ } else if (context.getDefinition().isD8R8Synthesized()) {
+ // TODO(b/261971803): Enable assert.
+ assert true || position.hasMethodInChain(originalMethod);
+ } else {
+ assert position.getOutermostCaller().getMethod() == originalMethod;
+ }
+ });
return true;
}
diff --git a/src/main/java/com/android/tools/r8/cf/CfVersion.java b/src/main/java/com/android/tools/r8/cf/CfVersion.java
index df6ee75..1fd5ce7 100644
--- a/src/main/java/com/android/tools/r8/cf/CfVersion.java
+++ b/src/main/java/com/android/tools/r8/cf/CfVersion.java
@@ -42,6 +42,8 @@
public static final CfVersion V18_PREVIEW = new CfVersion(Opcodes.V18 | Opcodes.V_PREVIEW);
public static final CfVersion V19 = new CfVersion(Opcodes.V19);
public static final CfVersion V19_PREVIEW = new CfVersion(Opcodes.V19 | Opcodes.V_PREVIEW);
+ public static final CfVersion V20 = new CfVersion(Opcodes.V20);
+ public static final CfVersion V20_PREVIEW = new CfVersion(Opcodes.V20 | Opcodes.V_PREVIEW);
private final int version;
@@ -64,7 +66,8 @@
CfVersion.V16,
CfVersion.V17,
CfVersion.V18,
- CfVersion.V19
+ CfVersion.V19,
+ CfVersion.V20
};
// Private constructor in case we want to canonicalize versions.
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 84334f5..c4e0493 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
@@ -223,8 +223,13 @@
type = Type.VIRTUAL;
canonicalMethod = method;
} else {
- type = Type.POLYMORPHIC;
- callSiteProto = method.proto;
+ if (builder.appView.options().shouldDesugarVarHandle()) {
+ type = Type.VIRTUAL;
+ canonicalMethod = method;
+ } else {
+ type = Type.POLYMORPHIC;
+ callSiteProto = method.proto;
+ }
}
break;
}
diff --git a/src/main/java/com/android/tools/r8/graph/ApplicationReaderMap.java b/src/main/java/com/android/tools/r8/graph/ApplicationReaderMap.java
index cec1de3..34898725 100644
--- a/src/main/java/com/android/tools/r8/graph/ApplicationReaderMap.java
+++ b/src/main/java/com/android/tools/r8/graph/ApplicationReaderMap.java
@@ -5,8 +5,6 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.ImmutableMap;
-import java.util.Map;
public abstract class ApplicationReaderMap {
@@ -23,9 +21,6 @@
if (options.shouldDesugarRecords() && !options.testing.disableRecordApplicationReaderMap) {
result = new RecordMap(options.dexItemFactory());
}
- if (options.shouldDesugarVarHandle()) {
- return new VarHandleMap(result);
- }
return result;
}
@@ -72,34 +67,4 @@
return type == factory.recordType ? factory.recordTagType : type;
}
}
-
- public static class VarHandleMap extends ApplicationReaderMap {
-
- private final ApplicationReaderMap previous;
- private final Map<String, String> descriptorMap =
- ImmutableMap.of(
- DexItemFactory.varHandleDescriptorString,
- DexItemFactory.desugarVarHandleDescriptorString,
- DexItemFactory.methodHandlesLookupDescriptorString,
- DexItemFactory.desugarMethodHandlesLookupDescriptorString);
-
- public VarHandleMap(ApplicationReaderMap previous) {
- this.previous = previous;
- }
-
- @Override
- public String getDescriptor(String descriptor) {
- return previous.getDescriptor(descriptorMap.getOrDefault(descriptor, descriptor));
- }
-
- @Override
- public DexType getType(DexType type) {
- return type;
- }
-
- @Override
- public DexType getInvertedType(DexType type) {
- return type;
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
index 1d05d43..f360949 100644
--- a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
@@ -62,7 +62,7 @@
// Record original method signatures.
for (DexEncodedMethod encodedMethod : clazz.methods()) {
DexMethod method = encodedMethod.getReference();
- DexMethod original = appView.graphLens().getOriginalMethodSignature(method);
+ DexMethod original = appView.graphLens().getOriginalMethodSignatureForMapping(method);
DexMethod existing = originalMethodSignatures.inverse().get(original);
if (existing == null) {
originalMethodSignatures.put(method, original);
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 169a880..fff7a6e 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -59,6 +59,7 @@
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
+import java.util.function.Consumer;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
@@ -972,4 +973,13 @@
.build();
return helper.run();
}
+
+ @Override
+ public void forEachPosition(Consumer<Position> positionConsumer) {
+ for (CfInstruction instruction : getInstructions()) {
+ if (instruction.isPosition()) {
+ positionConsumer.accept(instruction.asPosition().getPosition());
+ }
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index 0475e2c..bc0c72b 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.RetracerForCodePrinting;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import java.util.function.Consumer;
public abstract class Code extends CachedHashValueDexItem {
@@ -190,7 +191,7 @@
Position callerPosition, Position oldPosition, boolean isCalleeD8R8Synthesized) {
Position outermostCaller = oldPosition.getOutermostCaller();
if (!isCalleeD8R8Synthesized) {
- return oldPosition.withOutermostCallerPosition(callerPosition);
+ return removeSameMethodAndLineZero(oldPosition, callerPosition);
}
// We can replace the position since the callee was synthesized by the compiler, however, if
// the position carries special information we need to copy it.
@@ -208,4 +209,23 @@
}
return oldPosition.replacePosition(outermostCaller, positionBuilder.build());
}
+
+ @Deprecated()
+ // TODO(b/261971803): When having complete control over the positions we should not need this.
+ private static Position removeSameMethodAndLineZero(
+ Position calleePosition, Position callerPosition) {
+ Position outermostCaller = calleePosition.getOutermostCaller();
+ if (outermostCaller.getLine() == 0) {
+ while (callerPosition != null
+ && outermostCaller.getMethod() == callerPosition.getMethod()
+ && callerPosition.getLine() == 0) {
+ callerPosition = callerPosition.getCallerPosition();
+ }
+ }
+ return calleePosition.withOutermostCallerPosition(callerPosition);
+ }
+
+ public void forEachPosition(Consumer<Position> positionConsumer) {
+ // Intentionally empty. Override where we have fully build CF or DEX code.
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplicationReadFlags.java b/src/main/java/com/android/tools/r8/graph/DexApplicationReadFlags.java
index b64ef99..f949455 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplicationReadFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplicationReadFlags.java
@@ -44,6 +44,14 @@
return recordWitnesses;
}
+ public boolean hasReadMethodHandlesLookupReferenceFromProgramClass() {
+ return !methodHandlesLookupWitnesses.isEmpty();
+ }
+
+ public Set<DexType> getMethodHandlesLookupWitnesses() {
+ return methodHandlesLookupWitnesses;
+ }
+
public boolean hasReadVarHandleReferenceFromProgramClass() {
return !varHandleWitnesses.isEmpty();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index 8ca811b..60ce8b5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -52,6 +52,7 @@
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
+import java.util.function.Consumer;
// DexCode corresponds to code item in dalvik/dex-format.html
public class DexCode extends Code implements DexWritableCode, StructuralItem<DexCode> {
@@ -775,6 +776,18 @@
}
}
+ @Override
+ public void forEachPosition(Consumer<Position> positionConsumer) {
+ if (getDebugInfo() == null || getDebugInfo().isPcBasedInfo()) {
+ return;
+ }
+ for (DexDebugEvent event : getDebugInfo().asEventBasedInfo().events) {
+ if (event.isPositionFrame()) {
+ positionConsumer.accept(event.asSetPositionFrame().getPosition());
+ }
+ }
+ }
+
public static class Try extends DexItem implements StructuralItem<Try> {
public static final Try[] EMPTY_ARRAY = new Try[0];
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 318b353..761298d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -1093,12 +1093,39 @@
public DexEncodedMethod toTypeSubstitutedMethod(DexMethod method) {
checkIfObsolete();
- return toTypeSubstitutedMethod(method, null);
+ return toTypeSubstitutedMethodHelper(method, isD8R8Synthesized(), null);
}
public DexEncodedMethod toTypeSubstitutedMethod(DexMethod method, Consumer<Builder> consumer) {
checkIfObsolete();
- Builder builder = builder(this);
+ return toTypeSubstitutedMethodHelper(method, isD8R8Synthesized(), consumer);
+ }
+
+ public DexEncodedMethod toTypeSubstitutedMethodAsInlining(
+ DexMethod method, DexItemFactory factory) {
+ checkIfObsolete();
+ return toTypeSubstitutedMethodAsInlining(method, factory, null);
+ }
+
+ public DexEncodedMethod toTypeSubstitutedMethodAsInlining(
+ DexMethod method, DexItemFactory factory, Consumer<Builder> consumer) {
+ return toTypeSubstitutedMethodHelper(
+ method,
+ true,
+ builder -> {
+ if (code != null) {
+ builder.setCode(getCode().getCodeAsInlining(method, this, factory));
+ }
+ if (consumer != null) {
+ consumer.accept(builder);
+ }
+ });
+ }
+
+ private DexEncodedMethod toTypeSubstitutedMethodHelper(
+ DexMethod method, boolean isD8R8Synthesized, Consumer<Builder> consumer) {
+ checkIfObsolete();
+ Builder builder = isD8R8Synthesized ? syntheticBuilder(this) : builder(this);
if (isNonPrivateVirtualMethod() && isLibraryMethodOverride() != OptionalBool.unknown()) {
builder.setIsLibraryMethodOverride(isLibraryMethodOverride());
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 94b8840..57d6d34 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -80,8 +80,6 @@
public static final String varHandleDescriptorString = "Ljava/lang/invoke/VarHandle;";
public static final String desugarMethodHandlesLookupDescriptorString =
"Lcom/android/tools/r8/DesugarMethodHandlesLookup;";
- public static final String methodHandlesLookupDescriptorString =
- "Ljava/lang/invoke/MethodHandles$Lookup;";
public static final String dalvikAnnotationOptimizationPrefixString =
"Ldalvik/annotation/optimization/";
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index 46d91ef..43558a8 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -329,6 +329,18 @@
return original;
}
+ public final DexMethod getOriginalMethodSignatureForMapping(DexMethod method) {
+ GraphLens current = this;
+ DexMethod original = method;
+ while (current.isNonIdentityLens()) {
+ NonIdentityGraphLens nonIdentityLens = current.asNonIdentityLens();
+ original = nonIdentityLens.getPreviousMethodSignatureForMapping(original);
+ current = nonIdentityLens.getPrevious();
+ }
+ assert current.isIdentityLens();
+ return original;
+ }
+
public final DexField getRenamedFieldSignature(DexField originalField) {
return getRenamedFieldSignature(originalField, null);
}
@@ -971,6 +983,15 @@
public abstract DexMethod getPreviousMethodSignature(DexMethod method);
+ /***
+ * The previous mapping for a method often coincides with the previous method signature, but it
+ * may not, for example for bridges inserted in vertically merged classes where the original
+ * signature is used for computing invoke-super but should not be used for mapping output.
+ */
+ public DexMethod getPreviousMethodSignatureForMapping(DexMethod method) {
+ return getPreviousMethodSignature(method);
+ }
+
public abstract DexMethod getNextMethodSignature(DexMethod method);
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Invoke.java b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
index 80bb77b..18624c5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Invoke.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
@@ -86,6 +86,7 @@
return Type.STATIC;
case Opcodes.INVOKEVIRTUAL:
return appView.dexItemFactory().polymorphicMethods.isPolymorphicInvoke(invokedMethod)
+ && !appView.options().shouldDesugarVarHandle()
? Type.POLYMORPHIC
: Type.VIRTUAL;
default:
@@ -94,11 +95,6 @@
}
public static Type fromInvokeSpecial(
- DexMethod invokedMethod, DexClassAndMethod context, AppView<?> appView) {
- return fromInvokeSpecial(invokedMethod, context, appView, appView.codeLens());
- }
-
- public static Type fromInvokeSpecial(
DexMethod invokedMethod,
DexClassAndMethod context,
AppView<?> appView,
diff --git a/src/main/java/com/android/tools/r8/ir/code/Position.java b/src/main/java/com/android/tools/r8/ir/code/Position.java
index 5e88574..4c809dc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Position.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Position.java
@@ -33,13 +33,19 @@
protected final Position callerPosition;
private final boolean removeInnerFramesIfThrowingNpe;
+ private final boolean isD8R8Synthesized;
private Position(
- int line, DexMethod method, Position callerPosition, boolean removeInnerFramesIfThrowingNpe) {
+ int line,
+ DexMethod method,
+ Position callerPosition,
+ boolean removeInnerFramesIfThrowingNpe,
+ boolean isD8R8Synthesized) {
this.line = line;
this.method = method;
this.callerPosition = callerPosition;
this.removeInnerFramesIfThrowingNpe = removeInnerFramesIfThrowingNpe;
+ this.isD8R8Synthesized = isD8R8Synthesized;
}
public boolean isSyntheticPosition() {
@@ -50,10 +56,18 @@
return removeInnerFramesIfThrowingNpe;
}
+ public boolean isD8R8Synthesized() {
+ return isD8R8Synthesized;
+ }
+
public boolean isOutline() {
return false;
}
+ public boolean isOutlineCaller() {
+ return false;
+ }
+
public DexMethod getOutlineCallee() {
return null;
}
@@ -106,7 +120,8 @@
.withInt(Position::getLine)
.withNullableItem(Position::getMethod)
.withNullableItem(Position::getCallerPosition)
- .withBool(Position::isRemoveInnerFramesIfThrowingNpe);
+ .withBool(Position::isRemoveInnerFramesIfThrowingNpe)
+ .withBool(Position::isD8R8Synthesized);
}
public static Position syntheticNone() {
@@ -120,8 +135,17 @@
assert position.isNone();
position = SourcePosition.builder().setMethod(context.getReference()).build();
}
- assert position.getOutermostCaller().method
- == appView.graphLens().getOriginalMethodSignature(context.getReference());
+ if (context.getDefinition().isD8R8Synthesized()) {
+ // Some rewritings map a synthetic method back to an original in the program. To ensure we
+ // have correct line information we have to rewrite the positions as inline position
+ // therefore we only check if the original method is present.
+ DexMethod originalMethodSignature =
+ appView.graphLens().getOriginalMethodSignature(context.getReference());
+ assert position.hasMethodInChain(originalMethodSignature);
+ } else {
+ assert position.getOutermostCaller().method
+ == appView.graphLens().getOriginalMethodSignature(context.getReference());
+ }
return position;
}
@@ -167,6 +191,21 @@
return null;
}
+ public boolean hasPositionMatching(Predicate<Position> positionPredicate) {
+ Position lastPosition = this;
+ while (lastPosition != null) {
+ if (positionPredicate.test(lastPosition)) {
+ return true;
+ }
+ lastPosition = lastPosition.getCallerPosition();
+ }
+ return false;
+ }
+
+ public boolean hasMethodInChain(DexMethod method) {
+ return hasPositionMatching(position -> position.getMethod() == method);
+ }
+
public Position withOutermostCallerPosition(Position newOutermostCallerPosition) {
return builderWithCopy()
.setCallerPosition(
@@ -238,6 +277,7 @@
protected DexMethod method;
protected Position callerPosition;
protected boolean removeInnerFramesIfThrowingNpe;
+ protected boolean isD8R8Synthesized;
protected boolean noCheckOfPosition;
protected boolean noCheckOfMethod;
@@ -268,6 +308,11 @@
return self();
}
+ public B setIsD8R8Synthesized(boolean isD8R8Synthesized) {
+ this.isD8R8Synthesized = isD8R8Synthesized;
+ return self();
+ }
+
public B disableLineCheck() {
noCheckOfPosition = true;
return self();
@@ -286,7 +331,7 @@
// A no-position marker. Not having a position means the position is implicitly defined by the
// context, e.g., the marker does not materialize anything concrete.
private static final SourcePosition NO_POSITION =
- new SourcePosition(-1, null, null, false, null);
+ new SourcePosition(-1, null, null, false, false, null);
public final DexString file;
@@ -299,8 +344,9 @@
DexMethod method,
Position callerPosition,
boolean removeInnerFramesIfThrowingNpe,
+ boolean isD8R8Synthesized,
DexString file) {
- super(line, method, callerPosition, removeInnerFramesIfThrowingNpe);
+ super(line, method, callerPosition, removeInnerFramesIfThrowingNpe, isD8R8Synthesized);
this.file = file;
assert callerPosition == null || callerPosition.method != null;
}
@@ -327,7 +373,8 @@
.setFile(file)
.setMethod(method)
.setCallerPosition(callerPosition)
- .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe());
+ .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe())
+ .setIsD8R8Synthesized(isD8R8Synthesized());
}
@Override
@@ -359,7 +406,7 @@
assert noCheckOfPosition || line >= 0;
assert noCheckOfMethod || method != null;
return new SourcePosition(
- line, method, callerPosition, removeInnerFramesIfThrowingNpe, file);
+ line, method, callerPosition, removeInnerFramesIfThrowingNpe, isD8R8Synthesized, file);
}
}
}
@@ -370,14 +417,15 @@
// This is used specifically to mark exceptional exit blocks from synchronized methods in
// release.
private static final Position NO_POSITION_SYNTHETIC =
- new SyntheticPosition(-1, null, null, false);
+ new SyntheticPosition(-1, null, null, false, false);
private SyntheticPosition(
int line,
DexMethod method,
Position callerPosition,
- boolean removeInnerFramesIfThrowingNpe) {
- super(line, method, callerPosition, removeInnerFramesIfThrowingNpe);
+ boolean removeInnerFramesIfThrowingNpe,
+ boolean isD8R8Synthesized) {
+ super(line, method, callerPosition, removeInnerFramesIfThrowingNpe, isD8R8Synthesized);
}
@Override
@@ -396,7 +444,8 @@
.setLine(line)
.setMethod(method)
.setCallerPosition(callerPosition)
- .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe());
+ .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe())
+ .setIsD8R8Synthesized(isD8R8Synthesized());
}
@Override
@@ -422,7 +471,8 @@
public SyntheticPosition build() {
assert noCheckOfPosition || line >= 0;
assert noCheckOfMethod || method != null;
- return new SyntheticPosition(line, method, callerPosition, removeInnerFramesIfThrowingNpe);
+ return new SyntheticPosition(
+ line, method, callerPosition, removeInnerFramesIfThrowingNpe, isD8R8Synthesized);
}
}
}
@@ -433,8 +483,9 @@
int line,
DexMethod method,
Position callerPosition,
- boolean removeInnerFramesIfThrowingNpe) {
- super(line, method, callerPosition, removeInnerFramesIfThrowingNpe);
+ boolean removeInnerFramesIfThrowingNpe,
+ boolean isD8R8Synthesized) {
+ super(line, method, callerPosition, removeInnerFramesIfThrowingNpe, isD8R8Synthesized);
}
@Override
@@ -453,7 +504,8 @@
.setLine(line)
.setMethod(method)
.setCallerPosition(callerPosition)
- .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe());
+ .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe())
+ .setIsD8R8Synthesized(isD8R8Synthesized());
}
@Override
@@ -477,7 +529,8 @@
@Override
public OutlinePosition build() {
- return new OutlinePosition(line, method, callerPosition, removeInnerFramesIfThrowingNpe);
+ return new OutlinePosition(
+ line, method, callerPosition, removeInnerFramesIfThrowingNpe, isD8R8Synthesized);
}
}
}
@@ -500,10 +553,11 @@
DexMethod method,
Position callerPosition,
boolean removeInnerFramesIfThrowingNpe,
+ boolean isD8R8Synthesized,
Int2StructuralItemArrayMap<Position> outlinePositions,
DexMethod outlineCallee,
boolean isOutline) {
- super(line, method, callerPosition, removeInnerFramesIfThrowingNpe);
+ super(line, method, callerPosition, removeInnerFramesIfThrowingNpe, isD8R8Synthesized);
this.outlinePositions = outlinePositions;
this.outlineCallee = outlineCallee;
this.isOutline = isOutline;
@@ -528,7 +582,8 @@
.setCallerPosition(callerPosition)
.setOutlineCallee(outlineCallee)
.setIsOutline(isOutline)
- .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe());
+ .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe())
+ .setIsD8R8Synthesized(isD8R8Synthesized());
outlinePositions.forEach(outlineCallerPositionBuilder::addOutlinePosition);
return outlineCallerPositionBuilder;
}
@@ -539,6 +594,11 @@
}
@Override
+ public boolean isOutlineCaller() {
+ return true;
+ }
+
+ @Override
public DexMethod getOutlineCallee() {
return outlineCallee;
}
@@ -600,6 +660,7 @@
method,
callerPosition,
removeInnerFramesIfThrowingNpe,
+ isD8R8Synthesized,
outlinePositionsBuilder.build(),
outlineCallee,
isOutline);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
index ea0f655..3242779 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -258,10 +258,11 @@
}
private Position getCanonicalPositionAppendCaller(DexDebugEntry entry) {
- // If this instruction has already been inlined then this.method must be the outermost caller.
+ // If this instruction has already been inlined then the original method must be in the caller
+ // chain.
Position position = entry.getPosition();
- assert !position.hasCallerPosition()
- || position.getOutermostCaller().getMethod() == originalMethod;
+ // TODO(b/261971803): The original method should probably always be in the chain.
+ assert !position.hasCallerPosition() || position.hasMethodInChain(originalMethod);
return canonicalPositions.getCanonical(
position
.builderWithCopy()
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 46266fc..c621494 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -663,7 +663,11 @@
.setGenericSignature(encodedMethod.getGenericSignature())
.setAnnotations(encodedMethod.annotations())
.setParameterAnnotations(encodedMethod.parameterAnnotationsList)
- .setCode(encodedMethod.getCode())
+ .setCode(
+ encodedMethod
+ .getCode()
+ .getCodeAsInlining(
+ callTarget, encodedMethod, appView.dexItemFactory()))
.setApiLevelForDefinition(encodedMethod.getApiLevelForDefinition())
.setApiLevelForCode(encodedMethod.getApiLevelForCode())
.build();
@@ -749,7 +753,11 @@
.setGenericSignature(encodedMethod.getGenericSignature())
.setAnnotations(encodedMethod.annotations())
.setParameterAnnotations(encodedMethod.parameterAnnotationsList)
- .setCode(encodedMethod.getCode())
+ .setCode(
+ encodedMethod
+ .getCode()
+ .getCodeAsInlining(
+ callTarget, encodedMethod, appView.dexItemFactory()))
.setApiLevelForDefinition(encodedMethod.getApiLevelForDefinition())
.setApiLevelForCode(encodedMethod.getApiLevelForCode())
.build();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java
index ae232bd..380d5d4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java
@@ -39,6 +39,10 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
import org.objectweb.asm.Opcodes;
public class VarHandleDesugaring implements CfInstructionDesugaring, CfClassSynthesizerDesugaring {
@@ -52,7 +56,6 @@
public static void registerSynthesizedCodeReferences(DexItemFactory factory) {
VarHandleDesugaringMethods.registerSynthesizedCodeReferences(factory);
- factory.createSynthesizedType(DexItemFactory.desugarMethodHandlesLookupDescriptorString);
}
public VarHandleDesugaring(AppView<?> appView) {
@@ -63,7 +66,7 @@
@Override
public void scan(
ProgramMethod programMethod, CfInstructionDesugaringEventConsumer eventConsumer) {
- if (programMethod.getHolderType() == factory.desugarVarHandleType) {
+ if (programMethod.getHolderType() == factory.varHandleType) {
return;
}
CfCode cfCode = programMethod.getDefinition().getCode().asCfCode();
@@ -90,12 +93,12 @@
}
private static boolean refersToVarHandle(DexType type, DexItemFactory factory) {
- if (type == factory.varHandleType) {
- // All references to java.lang.invoke.VarHandle is rewritten during application reading.
+ if (type == factory.desugarVarHandleType) {
+ // All references to java.lang.invoke.VarHandle is rewritten during application writing.
assert false;
return true;
}
- return type == factory.desugarVarHandleType;
+ return type == factory.varHandleType;
}
private static boolean refersToVarHandle(DexType[] types, DexItemFactory factory) {
@@ -130,13 +133,13 @@
}
private static boolean refersToMethodHandlesLookup(DexType type, DexItemFactory factory) {
- if (type == factory.methodHandlesLookupType) {
+ if (type == factory.desugarMethodHandlesLookupType) {
// All references to java.lang.invoke.MethodHandles$Lookup is rewritten during application
- // reading.
+ // writing.
assert false;
return true;
}
- return type == factory.desugarMethodHandlesLookupType;
+ return type == factory.methodHandlesLookupType;
}
private static boolean refersToMethodHandlesLookup(DexType[] types, DexItemFactory factory) {
@@ -164,7 +167,6 @@
public static boolean refersToMethodHandlesLookup(DexField field, DexItemFactory factory) {
if (refersToMethodHandlesLookup(field.holder, factory)) {
- assert false : "The MethodHandles$Lookup class has no fields.";
return true;
}
return refersToMethodHandlesLookup(field.type, factory);
@@ -177,7 +179,7 @@
.ensureGlobalClass(
() -> new MissingGlobalSyntheticsConsumerDiagnostic("VarHandle desugaring"),
kinds -> kinds.METHOD_HANDLES_LOOKUP,
- factory.desugarMethodHandlesLookupType,
+ factory.lookupType,
contexts,
appView,
builder ->
@@ -198,7 +200,7 @@
.ensureGlobalClass(
() -> new MissingGlobalSyntheticsConsumerDiagnostic("VarHandle desugaring"),
kinds -> kinds.VAR_HANDLE,
- factory.desugarVarHandleType,
+ factory.varHandleType,
contexts,
appView,
builder ->
@@ -245,13 +247,13 @@
DexType holder = invoke.getMethod().getHolderType();
if (holder != factory.methodHandlesType
&& holder != factory.methodHandlesLookupType
- && holder != factory.desugarVarHandleType) {
+ && holder != factory.varHandleType) {
return DesugarDescription.nothing();
}
DexMethod method = invoke.getMethod();
if (method.getHolderType() == factory.methodHandlesType) {
if (method.getName().equals(factory.createString("lookup"))
- && method.getReturnType() == factory.desugarMethodHandlesLookupType
+ && method.getReturnType() == factory.lookupType
&& method.getArity() == 0
&& invoke.isInvokeStatic()) {
return computeMethodHandlesLookup(factory);
@@ -260,18 +262,7 @@
}
}
- if (method.getHolderType() == factory.methodHandlesLookupType) {
- assert invoke.isInvokeVirtual();
-
- if (invoke.getMethod().getReturnType().equals(factory.desugarVarHandleType)) {
- return computeInvokeMethodHandleLookupMethodReturningVarHandle(factory, invoke);
- } else {
- assert invoke.getMethod().getReturnType().equals(factory.methodHandleType);
- return computeInvokeMethodHandleLookupMethodReturningMethodHandle(factory, invoke);
- }
- }
-
- if (method.getHolderType() == factory.desugarVarHandleType) {
+ if (method.getHolderType() == factory.varHandleType) {
assert invoke.isInvokeVirtual();
DexString name = method.getName();
int arity = method.getProto().getArity();
@@ -304,62 +295,18 @@
methodProcessingContext,
dexItemFactory) ->
ImmutableList.of(
- new CfNew(factory.desugarMethodHandlesLookupType),
+ new CfNew(factory.lookupType),
new CfStackInstruction(Opcode.Dup),
new CfInvoke(
Opcodes.INVOKESPECIAL,
factory.createMethod(
- factory.desugarMethodHandlesLookupType,
+ factory.lookupType,
factory.createProto(factory.voidType),
factory.constructorMethodName),
false)))
.build();
}
- public DesugarDescription computeInvokeMethodHandleLookupMethodReturningVarHandle(
- DexItemFactory factory, CfInvoke invoke) {
- return DesugarDescription.builder()
- .setDesugarRewrite(
- (freshLocalProvider,
- localStackAllocator,
- eventConsumer,
- context,
- methodProcessingContext,
- dexItemFactory) ->
- ImmutableList.of(
- new CfInvoke(
- Opcodes.INVOKEVIRTUAL,
- factory.createMethod(
- factory.desugarMethodHandlesLookupType,
- factory.createProto(
- factory.desugarVarHandleType,
- invoke.getMethod().getProto().getParameters()),
- invoke.getMethod().getName()),
- false)))
- .build();
- }
-
- public DesugarDescription computeInvokeMethodHandleLookupMethodReturningMethodHandle(
- DexItemFactory factory, CfInvoke invoke) {
- return DesugarDescription.builder()
- .setDesugarRewrite(
- (freshLocalProvider,
- localStackAllocator,
- eventConsumer,
- context,
- methodProcessingContext,
- dexItemFactory) ->
- ImmutableList.of(
- new CfInvoke(
- Opcodes.INVOKEVIRTUAL,
- factory.createMethod(
- factory.desugarMethodHandlesLookupType,
- invoke.getMethod().getProto(),
- invoke.getMethod().getName()),
- false)))
- .build();
- }
-
public DesugarDescription computeDesugarSignaturePolymorphicMethod(
CfInvoke invoke, int coordinates) {
return DesugarDescription.builder()
@@ -463,7 +410,7 @@
? proto.returnType
: objectOrPrimitiveReturnType(proto.returnType),
newParameters);
- DexMethod newMethod = factory.createMethod(factory.desugarVarHandleType, newProto, name);
+ DexMethod newMethod = factory.createMethod(factory.varHandleType, newProto, name);
builder.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, newMethod, false));
if (proto.returnType.isPrimitiveType() && !newProto.returnType.isPrimitiveType()) {
assert proto.returnType.isPrimitiveType();
@@ -487,20 +434,38 @@
@Override
// TODO(b/247076137): Is synthesizeClasses needed? Can DesugarVarHandle be created during
- // desugaring instead?
+ // desugaring instead? For R8 creating up-front and replacing the library definition seems to
+ // be the way to go.
public void synthesizeClasses(
ClassSynthesisDesugaringContext processingContext,
CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
DexApplicationReadFlags flags = appView.appInfo().app().getFlags();
- if (flags.hasReadVarHandleReferenceFromProgramClass()) {
+ synthesizeClassIfReferenced(
+ flags,
+ DexApplicationReadFlags::hasReadMethodHandlesLookupReferenceFromProgramClass,
+ DexApplicationReadFlags::getMethodHandlesLookupWitnesses,
+ classes -> ensureMethodHandlesLookupClass(eventConsumer, classes));
+ synthesizeClassIfReferenced(
+ flags,
+ DexApplicationReadFlags::hasReadVarHandleReferenceFromProgramClass,
+ DexApplicationReadFlags::getVarHandleWitnesses,
+ classes -> ensureVarHandleClass(eventConsumer, classes));
+ }
+
+ private void synthesizeClassIfReferenced(
+ DexApplicationReadFlags flags,
+ Predicate<DexApplicationReadFlags> hasReadReferenceFromProgramClass,
+ Function<DexApplicationReadFlags, Set<DexType>> getWitnesses,
+ Consumer<List<ProgramDefinition>> consumeProgramWitnesses) {
+ if (hasReadReferenceFromProgramClass.test(flags)) {
List<ProgramDefinition> classes = new ArrayList<>();
- for (DexType varHandleWitness : flags.getVarHandleWitnesses()) {
- DexClass dexClass = appView.contextIndependentDefinitionFor(varHandleWitness);
+ for (DexType witness : getWitnesses.apply(flags)) {
+ DexClass dexClass = appView.contextIndependentDefinitionFor(witness);
assert dexClass != null;
assert dexClass.isProgramClass();
classes.add(dexClass.asProgramClass());
}
- ensureVarHandleClass(eventConsumer, classes);
+ consumeProgramWitnesses.accept(classes);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringMethods.java
index 112f246..90c8eb5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringMethods.java
@@ -50,13 +50,14 @@
public final class VarHandleDesugaringMethods {
public static void registerSynthesizedCodeReferences(DexItemFactory factory) {
- factory.createSynthesizedType("Lcom/android/tools/r8/DesugarVarHandle;");
factory.createSynthesizedType("Ljava/lang/Byte;");
factory.createSynthesizedType("Ljava/lang/ClassCastException;");
factory.createSynthesizedType("Ljava/lang/Integer;");
factory.createSynthesizedType("Ljava/lang/Long;");
factory.createSynthesizedType("Ljava/lang/RuntimeException;");
factory.createSynthesizedType("Ljava/lang/Short;");
+ factory.createSynthesizedType("Ljava/lang/UnsupportedOperationException;");
+ factory.createSynthesizedType("Ljava/lang/invoke/VarHandle;");
factory.createSynthesizedType("Ljava/lang/reflect/Field;");
factory.createSynthesizedType("Lsun/misc/Unsafe;");
}
@@ -305,7 +306,7 @@
factory.createMethod(
builder.getType(),
factory.createProto(
- factory.createType(factory.createString("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType(factory.createString("Ljava/lang/invoke/VarHandle;")),
factory.createType(factory.createString("Ljava/lang/Class;")),
factory.createType(factory.createString("Ljava/lang/String;")),
factory.createType(factory.createString("Ljava/lang/Class;"))),
@@ -371,7 +372,7 @@
4,
ImmutableList.of(
label0,
- new CfNew(factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ new CfNew(factory.createType("Ljava/lang/invoke/VarHandle;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfLoad(ValueType.OBJECT, 1),
new CfLoad(ValueType.OBJECT, 2),
@@ -379,7 +380,7 @@
new CfInvoke(
183,
factory.createMethod(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createProto(
factory.voidType, factory.classType, factory.stringType, factory.classType),
factory.createString("<init>")),
@@ -450,7 +451,7 @@
new CfCheckCast(factory.createType("Lsun/misc/Unsafe;")),
new CfInstanceFieldWrite(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
label4,
@@ -458,7 +459,7 @@
new CfLoad(ValueType.OBJECT, 1),
new CfInstanceFieldWrite(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.classType,
factory.createString("recv"))),
label5,
@@ -473,7 +474,7 @@
false),
new CfInstanceFieldWrite(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.classType,
factory.createString("type"))),
label6,
@@ -481,13 +482,13 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.classType,
factory.createString("recv"))),
new CfInvoke(
@@ -500,7 +501,7 @@
new CfNumberConversion(NumericType.INT, NumericType.LONG),
new CfInstanceFieldWrite(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.longType,
factory.createString("offset"))),
label7,
@@ -521,6 +522,9 @@
CfLabel label7 = new CfLabel();
CfLabel label8 = new CfLabel();
CfLabel label9 = new CfLabel();
+ CfLabel label10 = new CfLabel();
+ CfLabel label11 = new CfLabel();
+ CfLabel label12 = new CfLabel();
return new CfCode(
method.holder,
4,
@@ -571,7 +575,7 @@
new CfCheckCast(factory.createType("Lsun/misc/Unsafe;")),
new CfInstanceFieldWrite(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
label4,
@@ -579,7 +583,7 @@
new CfLoad(ValueType.OBJECT, 1),
new CfInstanceFieldWrite(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.classType,
factory.createString("recv"))),
label5,
@@ -606,15 +610,116 @@
false),
new CfInstanceFieldWrite(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.classType,
factory.createString("type"))),
label7,
+ new CfLoad(ValueType.OBJECT, 3),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.classType,
+ factory.createProto(factory.booleanType),
+ factory.createString("isPrimitive")),
+ false),
+ new CfIf(If.Type.EQ, ValueType.INT, label10),
+ new CfLoad(ValueType.OBJECT, 3),
+ new CfStaticFieldRead(
+ factory.createField(
+ factory.createType("Ljava/lang/Integer;"),
+ factory.classType,
+ factory.createString("TYPE"))),
+ new CfIfCmp(If.Type.EQ, ValueType.OBJECT, label10),
+ new CfLoad(ValueType.OBJECT, 3),
+ new CfStaticFieldRead(
+ factory.createField(
+ factory.createType("Ljava/lang/Long;"),
+ factory.classType,
+ factory.createString("TYPE"))),
+ new CfIfCmp(If.Type.EQ, ValueType.OBJECT, label10),
+ label8,
+ new CfNew(factory.createType("Ljava/lang/UnsupportedOperationException;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfNew(factory.stringBuilderType),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfInvoke(
+ 183,
+ factory.createMethod(
+ factory.stringBuilderType,
+ factory.createProto(factory.voidType),
+ factory.createString("<init>")),
+ false),
+ new CfConstString(factory.createString("Using a VarHandle for a field of type '")),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.stringBuilderType,
+ factory.createProto(factory.stringBuilderType, factory.stringType),
+ factory.createString("append")),
+ false),
+ new CfLoad(ValueType.OBJECT, 3),
+ label9,
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.classType,
+ factory.createProto(factory.stringType),
+ factory.createString("getName")),
+ false),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.stringBuilderType,
+ factory.createProto(factory.stringBuilderType, factory.stringType),
+ factory.createString("append")),
+ false),
+ new CfConstString(
+ factory.createString(
+ "' requires native VarHandle support available from Android 13. VarHandle"
+ + " desugaring only supports primitive types int and long and reference"
+ + " types.")),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.stringBuilderType,
+ factory.createProto(factory.stringBuilderType, factory.stringType),
+ factory.createString("append")),
+ false),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.stringBuilderType,
+ factory.createProto(factory.stringType),
+ factory.createString("toString")),
+ false),
+ new CfInvoke(
+ 183,
+ factory.createMethod(
+ factory.createType("Ljava/lang/UnsupportedOperationException;"),
+ factory.createProto(factory.voidType, factory.stringType),
+ factory.createString("<init>")),
+ false),
+ new CfThrow(),
+ label10,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1, 2, 3, 4, 5},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
+ FrameType.initializedNonNullReference(factory.classType),
+ FrameType.initializedNonNullReference(factory.stringType),
+ FrameType.initializedNonNullReference(factory.classType),
+ FrameType.initializedNonNullReference(
+ factory.createType("Ljava/lang/reflect/Field;")),
+ FrameType.initializedNonNullReference(
+ factory.createType("Ljava/lang/reflect/Field;"))
+ })),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 1),
@@ -637,12 +742,12 @@
false),
new CfInstanceFieldWrite(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.longType,
factory.createString("offset"))),
- label8,
+ label11,
new CfReturnVoid(),
- label9),
+ label12),
ImmutableList.of(),
ImmutableList.of());
}
@@ -667,7 +772,7 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.classType,
factory.createString("type"))),
new CfStaticFieldRead(
@@ -680,14 +785,14 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 1),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.longType,
factory.createString("offset"))),
new CfLoad(ValueType.OBJECT, 0),
@@ -697,7 +802,7 @@
new CfInvoke(
182,
factory.createMethod(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createProto(factory.intType, factory.objectType, factory.booleanType),
factory.createString("toIntIfPossible")),
false),
@@ -707,7 +812,7 @@
new CfInvoke(
182,
factory.createMethod(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createProto(factory.intType, factory.objectType, factory.booleanType),
factory.createString("toIntIfPossible")),
false),
@@ -731,7 +836,7 @@
new int[] {0, 1, 2, 3},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType),
FrameType.initializedNonNullReference(factory.objectType),
FrameType.initializedNonNullReference(factory.objectType)
@@ -739,7 +844,7 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.classType,
factory.createString("type"))),
new CfStaticFieldRead(
@@ -752,14 +857,14 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 1),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.longType,
factory.createString("offset"))),
new CfLoad(ValueType.OBJECT, 0),
@@ -769,7 +874,7 @@
new CfInvoke(
182,
factory.createMethod(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createProto(factory.longType, factory.objectType, factory.booleanType),
factory.createString("toLongIfPossible")),
false),
@@ -779,7 +884,7 @@
new CfInvoke(
182,
factory.createMethod(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createProto(factory.longType, factory.objectType, factory.booleanType),
factory.createString("toLongIfPossible")),
false),
@@ -803,7 +908,7 @@
new int[] {0, 1, 2, 3},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType),
FrameType.initializedNonNullReference(factory.objectType),
FrameType.initializedNonNullReference(factory.objectType)
@@ -811,14 +916,14 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 1),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.longType,
factory.createString("offset"))),
new CfLoad(ValueType.OBJECT, 2),
@@ -857,7 +962,7 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.classType,
factory.createString("type"))),
new CfStaticFieldRead(
@@ -870,14 +975,14 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 1),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.longType,
factory.createString("offset"))),
new CfLoad(ValueType.INT, 2),
@@ -901,7 +1006,7 @@
new int[] {0, 1, 2, 3},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType),
FrameType.intType(),
FrameType.intType()
@@ -909,7 +1014,7 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.classType,
factory.createString("type"))),
new CfStaticFieldRead(
@@ -922,14 +1027,14 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 1),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.longType,
factory.createString("offset"))),
new CfLoad(ValueType.INT, 2),
@@ -955,7 +1060,7 @@
new int[] {0, 1, 2, 3},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType),
FrameType.intType(),
FrameType.intType()
@@ -981,7 +1086,7 @@
new CfInvoke(
182,
factory.createMethod(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createProto(
factory.booleanType,
factory.objectType,
@@ -1010,7 +1115,7 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.classType,
factory.createString("type"))),
new CfStaticFieldRead(
@@ -1023,14 +1128,14 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 1),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.longType,
factory.createString("offset"))),
new CfLoad(ValueType.LONG, 2),
@@ -1054,7 +1159,7 @@
new int[] {0, 1, 2, 3, 4, 5},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType),
FrameType.longType(),
FrameType.longHighType(),
@@ -1082,7 +1187,7 @@
new CfInvoke(
182,
factory.createMethod(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createProto(
factory.booleanType,
factory.objectType,
@@ -1138,7 +1243,7 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.classType,
factory.createString("type"))),
new CfStaticFieldRead(
@@ -1151,14 +1256,14 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 1),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.longType,
factory.createString("offset"))),
new CfInvoke(
@@ -1182,13 +1287,13 @@
new int[] {0, 1},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.classType,
factory.createString("type"))),
new CfStaticFieldRead(
@@ -1201,14 +1306,14 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 1),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.longType,
factory.createString("offset"))),
new CfInvoke(
@@ -1232,20 +1337,20 @@
new int[] {0, 1},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 1),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.longType,
factory.createString("offset"))),
new CfInvoke(
@@ -1277,7 +1382,7 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.classType,
factory.createString("type"))),
new CfStaticFieldRead(
@@ -1290,14 +1395,14 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 1),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.longType,
factory.createString("offset"))),
new CfInvoke(
@@ -1314,13 +1419,13 @@
new int[] {0, 1},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.classType,
factory.createString("type"))),
new CfStaticFieldRead(
@@ -1333,14 +1438,14 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 1),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.longType,
factory.createString("offset"))),
new CfInvoke(
@@ -1358,21 +1463,21 @@
new int[] {0, 1},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 1),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.longType,
factory.createString("offset"))),
new CfInvoke(
@@ -1386,7 +1491,7 @@
new CfInvoke(
182,
factory.createMethod(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createProto(factory.intType, factory.objectType, factory.booleanType),
factory.createString("toIntIfPossible")),
false),
@@ -1412,7 +1517,7 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.classType,
factory.createString("type"))),
new CfStaticFieldRead(
@@ -1425,14 +1530,14 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 1),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.longType,
factory.createString("offset"))),
new CfInvoke(
@@ -1449,13 +1554,13 @@
new int[] {0, 1},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.classType,
factory.createString("type"))),
new CfStaticFieldRead(
@@ -1468,14 +1573,14 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 1),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.longType,
factory.createString("offset"))),
new CfInvoke(
@@ -1493,21 +1598,21 @@
new int[] {0, 1},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 1),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.longType,
factory.createString("offset"))),
new CfInvoke(
@@ -1521,7 +1626,7 @@
new CfInvoke(
182,
factory.createMethod(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createProto(factory.longType, factory.objectType, factory.booleanType),
factory.createString("toLongIfPossible")),
false),
@@ -1548,7 +1653,7 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.classType,
factory.createString("type"))),
new CfStaticFieldRead(
@@ -1566,14 +1671,14 @@
new CfInvoke(
182,
factory.createMethod(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createProto(factory.intType, factory.objectType, factory.booleanType),
factory.createString("toIntIfPossible")),
false),
new CfInvoke(
182,
factory.createMethod(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createProto(factory.voidType, factory.objectType, factory.intType),
factory.createString("set")),
false),
@@ -1584,14 +1689,14 @@
new int[] {0, 1, 2},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType),
FrameType.initializedNonNullReference(factory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.classType,
factory.createString("type"))),
new CfStaticFieldRead(
@@ -1609,14 +1714,14 @@
new CfInvoke(
182,
factory.createMethod(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createProto(factory.longType, factory.objectType, factory.booleanType),
factory.createString("toLongIfPossible")),
false),
new CfInvoke(
182,
factory.createMethod(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createProto(factory.voidType, factory.objectType, factory.longType),
factory.createString("set")),
false),
@@ -1627,21 +1732,21 @@
new int[] {0, 1, 2},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType),
FrameType.initializedNonNullReference(factory.objectType)
})),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 1),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.longType,
factory.createString("offset"))),
new CfLoad(ValueType.OBJECT, 2),
@@ -1659,7 +1764,7 @@
new int[] {0, 1, 2},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType),
FrameType.initializedNonNullReference(factory.objectType)
})),
@@ -1686,7 +1791,7 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.classType,
factory.createString("type"))),
new CfStaticFieldRead(
@@ -1699,14 +1804,14 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 1),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.longType,
factory.createString("offset"))),
new CfLoad(ValueType.INT, 2),
@@ -1725,14 +1830,14 @@
new int[] {0, 1, 2},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType),
FrameType.intType()
})),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.classType,
factory.createString("type"))),
new CfStaticFieldRead(
@@ -1745,14 +1850,14 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 1),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.longType,
factory.createString("offset"))),
new CfLoad(ValueType.INT, 2),
@@ -1772,7 +1877,7 @@
new int[] {0, 1, 2},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType),
FrameType.intType()
})),
@@ -1789,7 +1894,7 @@
new CfInvoke(
182,
factory.createMethod(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createProto(factory.voidType, factory.objectType, factory.objectType),
factory.createString("set")),
false),
@@ -1799,7 +1904,7 @@
new int[] {0, 1, 2},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType),
FrameType.intType()
})),
@@ -1826,7 +1931,7 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.classType,
factory.createString("type"))),
new CfStaticFieldRead(
@@ -1839,14 +1944,14 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 1),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.longType,
factory.createString("offset"))),
new CfLoad(ValueType.LONG, 2),
@@ -1865,7 +1970,7 @@
new int[] {0, 1, 2, 3},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType),
FrameType.longType(),
FrameType.longHighType()
@@ -1873,7 +1978,7 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.classType,
factory.createString("type"))),
new CfStaticFieldRead(
@@ -1887,7 +1992,7 @@
new CfInvoke(
182,
factory.createMethod(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createProto(factory.createType("Ljava/lang/RuntimeException;")),
factory.createString("desugarWrongMethodTypeException")),
false),
@@ -1898,7 +2003,7 @@
new int[] {0, 1, 2, 3},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType),
FrameType.longType(),
FrameType.longHighType()
@@ -1906,14 +2011,14 @@
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 1),
new CfLoad(ValueType.OBJECT, 0),
new CfInstanceFieldRead(
factory.createField(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.longType,
factory.createString("offset"))),
new CfLoad(ValueType.LONG, 2),
@@ -1938,7 +2043,7 @@
new int[] {0, 1, 2, 3},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType),
FrameType.longType(),
FrameType.longHighType()
@@ -1988,7 +2093,7 @@
new int[] {0, 1, 2},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType),
FrameType.intType()
})),
@@ -2012,7 +2117,7 @@
new int[] {0, 1, 2},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType),
FrameType.intType()
})),
@@ -2036,7 +2141,7 @@
new int[] {0, 1, 2},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType),
FrameType.intType()
})),
@@ -2060,7 +2165,7 @@
new int[] {0, 1, 2},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType),
FrameType.intType()
})),
@@ -2083,7 +2188,7 @@
new int[] {0, 1, 2},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType),
FrameType.intType()
})),
@@ -2091,7 +2196,7 @@
new CfInvoke(
182,
factory.createMethod(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createProto(factory.createType("Ljava/lang/RuntimeException;")),
factory.createString("desugarWrongMethodTypeException")),
false),
@@ -2132,7 +2237,7 @@
new int[] {0, 1, 2},
new FrameType[] {
FrameType.initializedNonNullReference(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ factory.createType("Ljava/lang/invoke/VarHandle;")),
FrameType.initializedNonNullReference(factory.objectType),
FrameType.intType()
})),
@@ -2142,7 +2247,7 @@
new CfInvoke(
182,
factory.createMethod(
- factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Ljava/lang/invoke/VarHandle;"),
factory.createProto(factory.intType, factory.objectType, factory.booleanType),
factory.createString("toIntIfPossible")),
false),
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index 7327231..d0db72d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -102,7 +102,8 @@
assert next.isArgument();
if (argumentInfo.isRewrittenTypeInfo()) {
RewrittenTypeInfo rewrittenTypeInfo = argumentInfo.asRewrittenTypeInfo();
- DexType enumType = getEnumTypeOrNull(rewrittenTypeInfo.getOldType().toBaseType(factory));
+ DexType enumType =
+ getEnumClassTypeOrNull(rewrittenTypeInfo.getOldType().toBaseType(factory));
if (enumType != null) {
convertedEnums.put(next, enumType);
}
@@ -142,7 +143,7 @@
if (instruction.isInitClass()) {
InitClass initClass = instruction.asInitClass();
- DexType enumType = getEnumTypeOrNull(initClass.getClassValue());
+ DexType enumType = getEnumClassTypeOrNull(initClass.getClassValue());
if (enumType != null) {
iterator.removeOrReplaceByDebugLocalRead();
}
@@ -154,7 +155,7 @@
if (!ifInstruction.isZeroTest()) {
for (int operandIndex = 0; operandIndex < 2; operandIndex++) {
Value operand = ifInstruction.getOperand(operandIndex);
- DexType enumType = getEnumTypeOrNull(operand, convertedEnums);
+ DexType enumType = getEnumClassTypeOrNull(operand, convertedEnums);
if (enumType != null) {
int otherOperandIndex = 1 - operandIndex;
Value otherOperand = ifInstruction.getOperand(otherOperandIndex);
@@ -179,7 +180,7 @@
// also in the unboxed enum class.
if (instruction.isInvokeMethodWithReceiver()) {
InvokeMethodWithReceiver invoke = instruction.asInvokeMethodWithReceiver();
- DexType enumType = getEnumTypeOrNull(invoke.getReceiver(), convertedEnums);
+ DexType enumType = getEnumClassTypeOrNull(invoke.getReceiver(), convertedEnums);
DexMethod invokedMethod = invoke.getInvokedMethod();
if (enumType != null) {
if (invokedMethod == factory.enumMembers.ordinalMethod
@@ -217,7 +218,7 @@
// Rewrites stringBuilder.append(enumInstance) as if it was
// stringBuilder.append(String.valueOf(unboxedEnumInstance));
Value enumArg = invoke.getArgument(1);
- DexType enumArgType = getEnumTypeOrNull(enumArg, convertedEnums);
+ DexType enumArgType = getEnumClassTypeOrNull(enumArg, convertedEnums);
if (enumArgType != null) {
ProgramMethod stringValueOfMethod =
getLocalUtilityClass(enumArgType).ensureStringValueOfMethod(appView);
@@ -330,7 +331,7 @@
// Rewrite array accesses from MyEnum[] (OBJECT) to int[] (INT).
if (instruction.isArrayAccess()) {
ArrayAccess arrayAccess = instruction.asArrayAccess();
- DexType enumType = getEnumTypeOrNull(arrayAccess, convertedEnums);
+ DexType enumType = getEnumArrayTypeOrNull(arrayAccess, convertedEnums);
if (enumType != null) {
if (arrayAccess.hasOutValue()) {
affectedPhis.addAll(arrayAccess.outValue().uniquePhiUsers());
@@ -403,14 +404,14 @@
if (invokedMethod == factory.objectsMethods.requireNonNull) {
assert invoke.arguments().size() == 1;
Value argument = invoke.getFirstArgument();
- DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
+ DexType enumType = getEnumClassTypeOrNull(argument, convertedEnums);
if (enumType != null) {
rewriteNullCheck(instructionIterator, invoke);
}
} else if (invokedMethod == factory.objectsMethods.requireNonNullWithMessage) {
assert invoke.arguments().size() == 2;
Value argument = invoke.getFirstArgument();
- DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
+ DexType enumType = getEnumClassTypeOrNull(argument, convertedEnums);
if (enumType != null) {
replaceEnumInvoke(
instructionIterator,
@@ -426,7 +427,7 @@
if (invokedMethod == factory.stringMembers.valueOf) {
assert invoke.arguments().size() == 1;
Value argument = invoke.getFirstArgument();
- DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
+ DexType enumType = getEnumClassTypeOrNull(argument, convertedEnums);
if (enumType != null) {
ProgramMethod stringValueOfMethod =
getLocalUtilityClass(enumType).ensureStringValueOfMethod(appView);
@@ -445,7 +446,7 @@
} else if (invokedMethod == factory.javaLangSystemMembers.identityHashCode) {
assert invoke.arguments().size() == 1;
Value argument = invoke.getFirstArgument();
- DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
+ DexType enumType = getEnumClassTypeOrNull(argument, convertedEnums);
if (enumType != null) {
invoke.outValue().replaceUsers(argument);
instructionIterator.removeOrReplaceByDebugLocalRead();
@@ -470,7 +471,7 @@
CheckNotNullEnumUnboxerMethodClassification checkNotNullClassification =
classification.asCheckNotNullClassification();
Value argument = invoke.getArgument(checkNotNullClassification.getArgumentIndex());
- DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
+ DexType enumType = getEnumClassTypeOrNull(argument, convertedEnums);
if (enumType != null) {
InvokeStatic replacement =
InvokeStatic.builder()
@@ -529,7 +530,7 @@
private Value fixNullsInBlockPhis(IRCode code, BasicBlock block, Value zeroConstValue) {
for (Phi phi : block.getPhis()) {
- if (getEnumTypeOrNull(phi.getType()) != null) {
+ if (getEnumClassTypeOrNull(phi.getType()) != null) {
for (int i = 0; i < phi.getOperands().size(); i++) {
Value operand = phi.getOperand(i);
if (operand.getType().isNullType()) {
@@ -585,26 +586,26 @@
return true;
}
- private DexType getEnumTypeOrNull(Value receiver, Map<Instruction, DexType> convertedEnums) {
+ private DexType getEnumClassTypeOrNull(Value receiver, Map<Instruction, DexType> convertedEnums) {
TypeElement type = receiver.getType();
- if (type.isInt() || (type.isArrayType() && type.asArrayType().getBaseType().isInt())) {
+ if (type.isInt()) {
return receiver.isPhi() ? null : convertedEnums.get(receiver.getDefinition());
}
- return getEnumTypeOrNull(type);
+ return getEnumClassTypeOrNull(type);
}
- private DexType getEnumTypeOrNull(TypeElement type) {
+ private DexType getEnumClassTypeOrNull(TypeElement type) {
if (!type.isClassType()) {
return null;
}
- return getEnumTypeOrNull(type.asClassType().getClassType());
+ return getEnumClassTypeOrNull(type.asClassType().getClassType());
}
- private DexType getEnumTypeOrNull(DexType type) {
+ private DexType getEnumClassTypeOrNull(DexType type) {
return unboxedEnumsData.isUnboxedEnum(type) ? type : null;
}
- private DexType getEnumTypeOrNull(
+ private DexType getEnumArrayTypeOrNull(
ArrayAccess arrayAccess, Map<Instruction, DexType> convertedEnums) {
ArrayTypeElement arrayType = arrayAccess.array().getType().asArrayType();
if (arrayType == null) {
@@ -616,9 +617,13 @@
}
TypeElement baseType = arrayType.getBaseType();
if (baseType.isClassType()) {
- DexType classType = baseType.asClassType().getClassType();
- return unboxedEnumsData.isUnboxedEnum(classType) ? classType : null;
+ return getEnumClassTypeOrNull(baseType.asClassType().getClassType());
}
- return getEnumTypeOrNull(arrayAccess.array(), convertedEnums);
+ if (arrayType.getBaseType().isInt()) {
+ return arrayAccess.array().isPhi()
+ ? null
+ : convertedEnums.get(arrayAccess.array().getDefinition());
+ }
+ return null;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
index 8e0d7d4..f9c13e9 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
@@ -70,6 +70,7 @@
.setLine(0)
.setMethod(originalMethod)
.setCallerPosition(callerPosition)
+ .setIsD8R8Synthesized(true)
.build();
}
diff --git a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
index b6ea11b..fc4fd33 100644
--- a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
+++ b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.naming.MappedRangeUtils.isInlineMappedRange;
import static com.android.tools.r8.utils.FunctionUtils.ignoreArgument;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRangesOfName;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
@@ -128,9 +129,8 @@
composingClassBuilder.compose(classNameMapper, classMapping);
}
-
- @Override
- public String toString() {
+ public String finish() {
+ committed.finish();
List<ComposingClassBuilder> classBuilders = new ArrayList<>(committed.classBuilders.values());
classBuilders.sort(Comparator.comparing(ComposingClassBuilder::getOriginalName));
StringBuilder sb = new StringBuilder();
@@ -167,6 +167,12 @@
private final Map<ClassTypeNameAndMethodName, UpdateOutlineCallsiteInformation>
outlineSourcePositionsUpdated = new HashMap<>();
+ /**
+ * Map of signatures that should be removed when finalizing the composed map. The key is the
+ * original name of a class.
+ */
+ private final Map<String, Set<Signature>> signaturesToRemove = new HashMap<>();
+
private final List<String> preamble = new ArrayList<>();
public void commit(ComposingData current, ClassNameMapper classNameMapper)
@@ -175,6 +181,7 @@
commitClassBuilders(current, classNameMapper);
commitRewriteFrameInformation(current, classNameMapper);
commitOutlineCallsiteInformation(current, classNameMapper);
+ commitSignaturesToRemove(current);
}
private void commitClassBuilders(ComposingData current, ClassNameMapper classNameMapper)
@@ -211,6 +218,28 @@
classBuilders = newClassBuilders;
}
+ private void commitSignaturesToRemove(ComposingData current) {
+ current.signaturesToRemove.forEach(
+ (originalName, signatures) -> {
+ signaturesToRemove.merge(
+ originalName,
+ signatures,
+ (signatures1, signatures2) -> {
+ Set<Signature> joinedSignatures = Sets.newHashSet(signatures1);
+ joinedSignatures.addAll(signatures2);
+ return joinedSignatures;
+ });
+ });
+ }
+
+ public void addSignatureToRemove(
+ ComposingClassBuilder composingClassBuilder, Signature signature) {
+ signaturesToRemove
+ .computeIfAbsent(
+ composingClassBuilder.getOriginalName(), ignoreArgument(Sets::newHashSet))
+ .add(signature);
+ }
+
private void commitRewriteFrameInformation(
ComposingData current, ClassNameMapper classNameMapper) {
// First update the existing frame information to have new class name mappings.
@@ -313,6 +342,25 @@
return newTypeName == null ? typeReference : Reference.classFromTypeName(newTypeName);
}
}
+
+ public void finish() {
+ classBuilders.forEach(
+ (ignored, classBuilder) -> {
+ Set<Signature> signatures = signaturesToRemove.get(classBuilder.getOriginalName());
+ if (signatures == null) {
+ return;
+ }
+ signatures.forEach(
+ signature -> {
+ if (signature.isFieldSignature()) {
+ classBuilder.fieldMembers.remove(signature.asFieldSignature());
+ } else {
+ // TODO(b/241763080): Define removal of methods with and without signatures.
+ throw new Unreachable();
+ }
+ });
+ });
+ }
}
private static class ClassTypeNameAndMethodName {
@@ -417,6 +465,7 @@
private final ComposingData committed;
private final ComposingData current;
+ private final Map<String, ComposingClassBuilder> committedPreviousClassBuilders;
private final ComposingClassBuilder committedPreviousClassBuilder;
private final InternalOptions options;
@@ -431,7 +480,8 @@
this.current = current;
this.committed = committed;
this.options = options;
- committedPreviousClassBuilder = committed.classBuilders.get(originalName);
+ committedPreviousClassBuilders = committed.classBuilders;
+ committedPreviousClassBuilder = committedPreviousClassBuilders.get(originalName);
}
public String getOriginalName() {
@@ -465,20 +515,46 @@
fieldNaming
.computeResidualSignature(type -> inverseClassMapping.getOrDefault(type, type))
.asFieldSignature();
- if (committedPreviousClassBuilder != null) {
- MemberNaming existingMemberNaming =
- committedPreviousClassBuilder.fieldMembers.remove(originalSignature);
- if (existingMemberNaming != null) {
- fieldNamingToAdd =
- new MemberNaming(
- existingMemberNaming.getOriginalSignature(), residualSignature);
+ MemberNaming existingMemberNaming = getExistingMemberNaming(originalSignature);
+ if (existingMemberNaming != null) {
+ Signature existingOriginalSignature = existingMemberNaming.getOriginalSignature();
+ if (!existingOriginalSignature.isQualified() && originalSignature.isQualified()) {
+ String previousCommittedClassName =
+ getPreviousCommittedClassName(originalSignature.toHolderFromQualified());
+ if (previousCommittedClassName != null) {
+ existingOriginalSignature =
+ existingOriginalSignature.toQualifiedSignature(previousCommittedClassName);
+ }
}
+ fieldNamingToAdd = new MemberNaming(existingOriginalSignature, residualSignature);
}
MemberNaming existing = fieldMembers.put(residualSignature, fieldNamingToAdd);
assert existing == null;
});
}
+ private String getPreviousCommittedClassName(String holder) {
+ ComposingClassBuilder composingClassBuilder = committedPreviousClassBuilders.get(holder);
+ return composingClassBuilder == null ? null : composingClassBuilder.getOriginalName();
+ }
+
+ private MemberNaming getExistingMemberNaming(FieldSignature originalSignature) {
+ ComposingClassBuilder composingClassBuilder =
+ originalSignature.isQualified()
+ ? committedPreviousClassBuilders.get(originalSignature.toHolderFromQualified())
+ : committedPreviousClassBuilder;
+ if (composingClassBuilder == null) {
+ return null;
+ }
+ FieldSignature signature =
+ (originalSignature.isQualified()
+ ? originalSignature.toUnqualifiedSignature()
+ : originalSignature)
+ .asFieldSignature();
+ current.addSignatureToRemove(composingClassBuilder, signature);
+ return composingClassBuilder.fieldMembers.get(signature);
+ }
+
private void composeMethodNamings(
ClassNamingForNameMapper mapper, ClassNameMapper classNameMapper)
throws MappingComposeException {
diff --git a/src/main/java/com/android/tools/r8/naming/MappingComposer.java b/src/main/java/com/android/tools/r8/naming/MappingComposer.java
index 636e699..2068902 100644
--- a/src/main/java/com/android/tools/r8/naming/MappingComposer.java
+++ b/src/main/java/com/android/tools/r8/naming/MappingComposer.java
@@ -23,6 +23,6 @@
for (ClassNameMapper classNameMapper : classNameMappers) {
builder.compose(classNameMapper);
}
- return builder.toString();
+ return builder.finish();
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNaming.java b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
index 12606ff..318034f 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
@@ -194,6 +194,10 @@
public abstract Signature computeResidualSignature(
String renamedName, Function<String, String> typeNameMapper);
+ public abstract Signature toUnqualifiedSignature();
+
+ public abstract Signature toQualifiedSignature(String holder);
+
public boolean isQualified() {
return name.indexOf(JAVA_PACKAGE_SEPARATOR) != -1;
}
@@ -294,6 +298,17 @@
}
@Override
+ public Signature toUnqualifiedSignature() {
+ return new FieldSignature(toUnqualifiedName(), type);
+ }
+
+ @Override
+ public Signature toQualifiedSignature(String holder) {
+ assert !isQualified();
+ return new FieldSignature(holder + "." + name, type);
+ }
+
+ @Override
public boolean equals(Object o) {
if (this == o) {
return true;
@@ -480,6 +495,16 @@
}
@Override
+ public Signature toUnqualifiedSignature() {
+ return new MethodSignature(toUnqualifiedName(), type, parameters);
+ }
+
+ @Override
+ public Signature toQualifiedSignature(String holder) {
+ return new MethodSignature(holder + "." + name, type, parameters);
+ }
+
+ @Override
public boolean isMethodSignature() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/naming/VarHandleDesugaringRewritingNamingLens.java b/src/main/java/com/android/tools/r8/naming/VarHandleDesugaringRewritingNamingLens.java
new file mode 100644
index 0000000..cc9c83f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/VarHandleDesugaringRewritingNamingLens.java
@@ -0,0 +1,103 @@
+// 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.naming;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.naming.NamingLens.NonIdentityNamingLens;
+import com.android.tools.r8.utils.InternalOptions;
+
+// Naming lens for VarHandle desugaring rewriting. Rewriting java.lang.invoke.MethodHandles$Lookup
+// to com.android.tools.r8.DesugarMethodHandlesLookup.
+public class VarHandleDesugaringRewritingNamingLens extends NonIdentityNamingLens {
+
+ private final DexItemFactory factory;
+ private final NamingLens namingLens;
+
+ public static NamingLens createVarHandleDesugaringRewritingNamingLens(AppView<?> appView) {
+ if (appView.options().shouldDesugarVarHandle()
+ && (appView
+ .appInfo()
+ .definitionForWithoutExistenceAssert(appView.dexItemFactory().lookupType)
+ != null
+ || appView
+ .appInfo()
+ .definitionForWithoutExistenceAssert(appView.dexItemFactory().varHandleType)
+ != null)) {
+ return new VarHandleDesugaringRewritingNamingLens(appView);
+ }
+ return appView.getNamingLens();
+ }
+
+ public VarHandleDesugaringRewritingNamingLens(AppView<?> appView) {
+ super(appView.dexItemFactory());
+ this.factory = appView.dexItemFactory();
+ this.namingLens = appView.getNamingLens();
+ }
+
+ private boolean isRenamed(DexType type) {
+ return getRenaming(type) != null;
+ }
+
+ private DexString getRenaming(DexType type) {
+ assert type != factory.desugarMethodHandlesLookupType;
+ assert type != factory.desugarVarHandleType;
+ if (type == factory.lookupType) {
+ return factory.desugarMethodHandlesLookupType.descriptor;
+ } else if (type == factory.varHandleType) {
+ return factory.desugarVarHandleType.descriptor;
+ }
+ return null;
+ }
+
+ @Override
+ protected DexString internalLookupClassDescriptor(DexType type) {
+ DexString renaming = getRenaming(type);
+ return renaming != null ? renaming : namingLens.lookupDescriptor(type);
+ }
+
+ @Override
+ public DexString lookupInnerName(InnerClassAttribute attribute, InternalOptions options) {
+ assert !isRenamed(attribute.getInner());
+ return namingLens.lookupInnerName(attribute, options);
+ }
+
+ @Override
+ public DexString lookupName(DexMethod method) {
+ // VarHandle desugaring rewriting does not influence method name.
+ return namingLens.lookupName(method);
+ }
+
+ @Override
+ public DexString lookupName(DexField field) {
+ // VarHandle desugaring rewriting does not influence method name.
+ return namingLens.lookupName(field);
+ }
+
+ @Override
+ public boolean hasPrefixRewritingLogic() {
+ return namingLens.hasPrefixRewritingLogic();
+ }
+
+ @Override
+ public DexString prefixRewrittenType(DexType type) {
+ return namingLens.prefixRewrittenType(type);
+ }
+
+ @Override
+ public String lookupPackageName(String packageName) {
+ return namingLens.lookupPackageName(packageName);
+ }
+
+ @Override
+ public boolean verifyRenamingConsistentWithResolution(DexMethod item) {
+ return namingLens.verifyRenamingConsistentWithResolution(item);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index b479808..32878da 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -65,6 +65,7 @@
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.code.Position.SyntheticPosition;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.MemberPoolCollection.MemberPool;
import com.android.tools.r8.ir.optimize.MethodPoolCollection;
@@ -2389,7 +2390,16 @@
.setTarget(invocationTarget)
.setInvokeType(type)
.setIsInterface(isInterface);
- return forwardSourceCodeBuilder::build;
+ return (context, callerPosition) -> {
+ SyntheticPosition caller =
+ SyntheticPosition.builder()
+ .setLine(0)
+ .setMethod(method)
+ .setIsD8R8Synthesized(true)
+ .setCallerPosition(callerPosition)
+ .build();
+ return forwardSourceCodeBuilder.build(context, caller);
+ };
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
index 05837f0..d0f940b 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
@@ -153,6 +153,12 @@
}
@Override
+ public DexMethod getPreviousMethodSignatureForMapping(DexMethod method) {
+ DexMethod orDefault = newMethodSignatures.getRepresentativeKeyOrDefault(method, method);
+ return super.getPreviousMethodSignature(orDefault);
+ }
+
+ @Override
protected Type mapInvocationType(DexMethod newMethod, DexMethod originalMethod, Type type) {
return mapVirtualInterfaceInvocationTypes(appView, newMethod, originalMethod, type);
}
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 38e6b3f..49455c1 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -149,7 +149,7 @@
}
}
- public static final CfVersion SUPPORTED_CF_VERSION = CfVersion.V19;
+ public static final CfVersion SUPPORTED_CF_VERSION = CfVersion.V20;
public static final int SUPPORTED_DEX_VERSION =
AndroidApiLevel.LATEST.getDexVersion().getIntValue();
diff --git a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
index 2915d14..c0a65b6 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
@@ -192,13 +192,15 @@
boolean canUseDexPc) {
DexEncodedMethod definition = method.getDefinition();
DexMethod originalMethod =
- appView.graphLens().getOriginalMethodSignature(method.getReference());
+ appView.graphLens().getOriginalMethodSignatureForMapping(method.getReference());
MethodSignature originalSignature =
MethodSignature.fromDexMethod(originalMethod, originalMethod.holder != originalType);
OneShotCollectionConsumer<MappingInformation> methodSpecificMappingInformation =
OneShotCollectionConsumer.wrap(new ArrayList<>());
- if (method.getDefinition().isD8R8Synthesized()) {
+ if (method.getDefinition().isD8R8Synthesized()
+ || (!mappedPositions.isEmpty()
+ && mappedPositions.get(0).getPosition().isD8R8Synthesized())) {
methodSpecificMappingInformation.add(
CompilerSynthesizedMappingInformation.builder().build());
}
@@ -309,11 +311,10 @@
appView,
getOriginalMethodSignature,
getBuilder(),
- firstPosition.getMethod(),
+ firstPosition,
residualSignature,
obfuscatedRange,
originalRange,
- firstPosition.getCallerPosition(),
prunedInlinedClasses,
cardinalRangeCache);
methodSpecificMappingInformation.consume(
@@ -338,12 +339,11 @@
appView,
getOriginalMethodSignature,
getBuilder(),
- position.getMethod(),
+ position,
residualSignature,
nonCardinalRangeCache.get(
placeHolderLineToBeFixed, placeHolderLineToBeFixed),
nonCardinalRangeCache.get(position.getLine(), position.getLine()),
- position.getCallerPosition(),
prunedInlinedClasses,
cardinalRangeCache);
});
@@ -370,37 +370,36 @@
AppView<?> appView,
Function<DexMethod, MethodSignature> getOriginalMethodSignature,
ClassNaming.Builder classNamingBuilder,
- DexMethod method,
+ Position position,
MethodSignature residualSignature,
Range obfuscatedRange,
Range originalLine,
- Position caller,
Map<DexType, String> prunedInlineHolder,
CardinalPositionRangeAllocator cardinalRangeCache) {
- MappedRange lastMappedRange =
- classNamingBuilder.addMappedRange(
- obfuscatedRange,
- getOriginalMethodSignature.apply(method),
- originalLine,
- residualSignature.getName());
- int inlineFramesCount = 0;
- while (caller != null) {
+ MappedRange lastMappedRange = null;
+ int inlineFramesCount = -1;
+ do {
+ if (position.isD8R8Synthesized() && position.hasCallerPosition()) {
+ position = position.getCallerPosition();
+ continue;
+ }
inlineFramesCount += 1;
- String prunedClassSourceFileInfo =
- appView.getPrunedClassSourceFileInfo(method.getHolderType());
+ DexType holderType = position.getMethod().getHolderType();
+ String prunedClassSourceFileInfo = appView.getPrunedClassSourceFileInfo(holderType);
if (prunedClassSourceFileInfo != null) {
- String originalValue =
- prunedInlineHolder.put(method.getHolderType(), prunedClassSourceFileInfo);
+ String originalValue = prunedInlineHolder.put(holderType, prunedClassSourceFileInfo);
assert originalValue == null || originalValue.equals(prunedClassSourceFileInfo);
}
lastMappedRange =
classNamingBuilder.addMappedRange(
obfuscatedRange,
- getOriginalMethodSignature.apply(caller.getMethod()),
- cardinalRangeCache.get(
- Math.max(caller.getLine(), 0)), // Prevent against "no-position".
+ getOriginalMethodSignature.apply(position.getMethod()),
+ inlineFramesCount == 0
+ ? originalLine
+ : cardinalRangeCache.get(
+ Math.max(position.getLine(), 0)), // Prevent against "no-position".
residualSignature.getName());
- if (caller.isRemoveInnerFramesIfThrowingNpe()) {
+ if (position.isRemoveInnerFramesIfThrowingNpe()) {
lastMappedRange.addMappingInformation(
RewriteFrameMappingInformation.builder()
.addCondition(
@@ -411,8 +410,9 @@
.build(),
Unreachable::raise);
}
- caller = caller.getCallerPosition();
- }
+ position = position.getCallerPosition();
+ } while (position != null);
+ assert lastMappedRange != null;
return lastMappedRange;
}
diff --git a/src/test/examplesJava9/varhandle/InstanceBooleanField.java b/src/test/examplesJava9/varhandle/InstanceBooleanField.java
new file mode 100644
index 0000000..89c034a
--- /dev/null
+++ b/src/test/examplesJava9/varhandle/InstanceBooleanField.java
@@ -0,0 +1,57 @@
+// 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 varhandle;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
+public class InstanceBooleanField {
+
+ private boolean field;
+
+ public static void testSet(VarHandle varHandle) {
+ System.out.println("testGet");
+
+ InstanceBooleanField instance = new InstanceBooleanField();
+ System.out.println(varHandle.get(instance));
+
+ // boolean and Boolean values.
+ varHandle.set(instance, true);
+ System.out.println((boolean) varHandle.get(instance));
+ varHandle.set(instance, Boolean.FALSE);
+ System.out.println(varHandle.get(instance));
+ }
+
+ public static void testCompareAndSet(VarHandle varHandle) {
+ System.out.println("testCompareAndSet");
+
+ InstanceBooleanField instance = new InstanceBooleanField();
+
+ // boolean and Boolean values.
+ varHandle.compareAndSet(instance, true, false);
+ System.out.println((boolean) varHandle.get(instance));
+ varHandle.compareAndSet(instance, false, true);
+ System.out.println((boolean) varHandle.get(instance));
+ varHandle.compareAndSet(instance, Boolean.TRUE, false);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, false, Boolean.TRUE);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, Boolean.TRUE, Boolean.FALSE);
+ System.out.println(varHandle.get(instance));
+ }
+
+ public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
+ VarHandle varHandle;
+ try {
+ varHandle =
+ MethodHandles.lookup().findVarHandle(InstanceBooleanField.class, "field", boolean.class);
+ } catch (UnsupportedOperationException e) {
+ System.out.println("Got UnsupportedOperationException");
+ return;
+ }
+
+ testSet(varHandle);
+ testCompareAndSet(varHandle);
+ }
+}
diff --git a/src/test/examplesJava9/varhandle/InstanceByteField.java b/src/test/examplesJava9/varhandle/InstanceByteField.java
new file mode 100644
index 0000000..242df89
--- /dev/null
+++ b/src/test/examplesJava9/varhandle/InstanceByteField.java
@@ -0,0 +1,57 @@
+// 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 varhandle;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
+public class InstanceByteField {
+
+ private byte field;
+
+ public static void testSet(VarHandle varHandle) {
+ System.out.println("testGet");
+
+ InstanceByteField instance = new InstanceByteField();
+ System.out.println(varHandle.get(instance));
+
+ // byte and Byte values.
+ varHandle.set(instance, (byte) 1);
+ System.out.println((byte) varHandle.get(instance));
+ varHandle.set(instance, Byte.valueOf((byte) 2));
+ System.out.println(varHandle.get(instance));
+ }
+
+ public static void testCompareAndSet(VarHandle varHandle) {
+ System.out.println("testCompareAndSet");
+
+ InstanceByteField instance = new InstanceByteField();
+
+ // byte and Byte values.
+ varHandle.compareAndSet(instance, (byte) 1, (byte) 2);
+ System.out.println((byte) varHandle.get(instance));
+ varHandle.compareAndSet(instance, (byte) 0, (byte) 1);
+ System.out.println((byte) varHandle.get(instance));
+ varHandle.compareAndSet(instance, Byte.valueOf((byte) 1), (byte) 2);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, (byte) 2, Byte.valueOf((byte) 3));
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, Byte.valueOf((byte) 3), Byte.valueOf((byte) 4));
+ System.out.println(varHandle.get(instance));
+ }
+
+ public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
+ VarHandle varHandle;
+ try {
+ varHandle =
+ MethodHandles.lookup().findVarHandle(InstanceByteField.class, "field", byte.class);
+ } catch (UnsupportedOperationException e) {
+ System.out.println("Got UnsupportedOperationException");
+ return;
+ }
+
+ testSet(varHandle);
+ testCompareAndSet(varHandle);
+ }
+}
diff --git a/src/test/examplesJava9/varhandle/InstanceDoubleField.java b/src/test/examplesJava9/varhandle/InstanceDoubleField.java
new file mode 100644
index 0000000..5b59463
--- /dev/null
+++ b/src/test/examplesJava9/varhandle/InstanceDoubleField.java
@@ -0,0 +1,57 @@
+// 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 varhandle;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
+public class InstanceDoubleField {
+
+ private double field;
+
+ public static void testSet(VarHandle varHandle) {
+ System.out.println("testGet");
+
+ InstanceDoubleField instance = new InstanceDoubleField();
+ System.out.println(varHandle.get(instance));
+
+ // double and Double values.
+ varHandle.set(instance, 1.0);
+ System.out.println((double) varHandle.get(instance));
+ varHandle.set(instance, Double.valueOf(2));
+ System.out.println(varHandle.get(instance));
+ }
+
+ public static void testCompareAndSet(VarHandle varHandle) {
+ System.out.println("testCompareAndSet");
+
+ InstanceDoubleField instance = new InstanceDoubleField();
+
+ // double and Double values.
+ varHandle.compareAndSet(instance, 1.0, 2.0);
+ System.out.println((double) varHandle.get(instance));
+ varHandle.compareAndSet(instance, 0.0, 1.0);
+ System.out.println((double) varHandle.get(instance));
+ varHandle.compareAndSet(instance, Double.valueOf(1), 2.0);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, 2.0, Double.valueOf(3));
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, Double.valueOf(3), Double.valueOf(4));
+ System.out.println(varHandle.get(instance));
+ }
+
+ public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
+ VarHandle varHandle;
+ try {
+ varHandle =
+ MethodHandles.lookup().findVarHandle(InstanceDoubleField.class, "field", double.class);
+ } catch (UnsupportedOperationException e) {
+ System.out.println("Got UnsupportedOperationException");
+ return;
+ }
+
+ testSet(varHandle);
+ testCompareAndSet(varHandle);
+ }
+}
diff --git a/src/test/examplesJava9/varhandle/InstanceFloatField.java b/src/test/examplesJava9/varhandle/InstanceFloatField.java
new file mode 100644
index 0000000..06960c4
--- /dev/null
+++ b/src/test/examplesJava9/varhandle/InstanceFloatField.java
@@ -0,0 +1,57 @@
+// 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 varhandle;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
+public class InstanceFloatField {
+
+ private float field;
+
+ public static void testSet(VarHandle varHandle) {
+ System.out.println("testGet");
+
+ InstanceFloatField instance = new InstanceFloatField();
+ System.out.println(varHandle.get(instance));
+
+ // float and Float values.
+ varHandle.set(instance, 1.0f);
+ System.out.println((float) varHandle.get(instance));
+ varHandle.set(instance, Float.valueOf(2));
+ System.out.println(varHandle.get(instance));
+ }
+
+ public static void testCompareAndSet(VarHandle varHandle) {
+ System.out.println("testCompareAndSet");
+
+ InstanceFloatField instance = new InstanceFloatField();
+
+ // float and Float values.
+ varHandle.compareAndSet(instance, 1.0f, 2.0f);
+ System.out.println((float) varHandle.get(instance));
+ varHandle.compareAndSet(instance, 0.0f, 1.0f);
+ System.out.println((float) varHandle.get(instance));
+ varHandle.compareAndSet(instance, Float.valueOf(1), 2.0f);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, 2.0f, Float.valueOf(3f));
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, Float.valueOf(3), Float.valueOf(4));
+ System.out.println(varHandle.get(instance));
+ }
+
+ public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
+ VarHandle varHandle;
+ try {
+ varHandle =
+ MethodHandles.lookup().findVarHandle(InstanceFloatField.class, "field", float.class);
+ } catch (UnsupportedOperationException e) {
+ System.out.println("Got UnsupportedOperationException");
+ return;
+ }
+
+ testSet(varHandle);
+ testCompareAndSet(varHandle);
+ }
+}
diff --git a/src/test/examplesJava9/varhandle/InstanceObjectField.java b/src/test/examplesJava9/varhandle/InstanceObjectField.java
index 1b4a4c9..c951ed8 100644
--- a/src/test/examplesJava9/varhandle/InstanceObjectField.java
+++ b/src/test/examplesJava9/varhandle/InstanceObjectField.java
@@ -20,7 +20,7 @@
}
public String toString() {
- return new StringBuilder().append("A(").append(i).append(")").toString();
+ return "A(" + i + ")";
}
}
diff --git a/src/test/examplesJava9/varhandle/InstanceShortField.java b/src/test/examplesJava9/varhandle/InstanceShortField.java
new file mode 100644
index 0000000..7890cf2
--- /dev/null
+++ b/src/test/examplesJava9/varhandle/InstanceShortField.java
@@ -0,0 +1,57 @@
+// 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 varhandle;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
+public class InstanceShortField {
+
+ private short field;
+
+ public static void testSet(VarHandle varHandle) {
+ System.out.println("testGet");
+
+ InstanceShortField instance = new InstanceShortField();
+ System.out.println(varHandle.get(instance));
+
+ // short and Short values.
+ varHandle.set(instance, (short) 1);
+ System.out.println((short) varHandle.get(instance));
+ varHandle.set(instance, Short.valueOf((short) 2));
+ System.out.println(varHandle.get(instance));
+ }
+
+ public static void testCompareAndSet(VarHandle varHandle) {
+ System.out.println("testCompareAndSet");
+
+ InstanceShortField instance = new InstanceShortField();
+
+ // short and Short values.
+ varHandle.compareAndSet(instance, (short) 1, (short) 2);
+ System.out.println((short) varHandle.get(instance));
+ varHandle.compareAndSet(instance, (short) 0, (short) 1);
+ System.out.println((short) varHandle.get(instance));
+ varHandle.compareAndSet(instance, Short.valueOf((short) 1), (short) 2);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, (short) 2, Short.valueOf((short) 3));
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, Short.valueOf((short) 3), Short.valueOf((short) 4));
+ System.out.println(varHandle.get(instance));
+ }
+
+ public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
+ VarHandle varHandle;
+ try {
+ varHandle =
+ MethodHandles.lookup().findVarHandle(InstanceShortField.class, "field", short.class);
+ } catch (UnsupportedOperationException e) {
+ System.out.println("Got UnsupportedOperationException");
+ return;
+ }
+
+ testSet(varHandle);
+ testCompareAndSet(varHandle);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 5c73e63..49a22e5 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -124,8 +124,8 @@
public static final String R8_TEST_BUCKET = "r8-test-results";
- public static final String ASM_JAR = BUILD_DIR + "deps/asm-9.3.jar";
- public static final String ASM_UTIL_JAR = BUILD_DIR + "deps/asm-util-9.3.jar";
+ public static final String ASM_JAR = BUILD_DIR + "deps/asm-9.4.jar";
+ public static final String ASM_UTIL_JAR = BUILD_DIR + "deps/asm-util-9.4.jar";
public static final Path API_SAMPLE_JAR = Paths.get("tests", "r8_api_usage_sample.jar");
diff --git a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceBooleanFieldTest.java b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceBooleanFieldTest.java
new file mode 100644
index 0000000..d9fc4c4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceBooleanFieldTest.java
@@ -0,0 +1,62 @@
+// 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.cf.varhandle;
+
+import com.android.tools.r8.examples.jdk9.VarHandle;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class VarHandleDesugaringInstanceBooleanFieldTest extends VarHandleDesugaringTestBase {
+
+ private static final String EXPECTED_OUTPUT =
+ StringUtils.lines(
+ "testGet",
+ "false",
+ "true",
+ "false",
+ "testCompareAndSet",
+ "false",
+ "true",
+ "false",
+ "true",
+ "false");
+
+ private static final String MAIN_CLASS = VarHandle.InstanceBooleanField.typeName();
+ private static final String JAR_ENTRY = "varhandle/InstanceBooleanField.class";
+
+ @Override
+ protected String getMainClass() {
+ return MAIN_CLASS;
+ }
+
+ @Override
+ protected String getKeepRules() {
+ return "-keep class " + getMainClass() + "{ <fields>; }";
+ }
+
+ @Override
+ protected List<String> getJarEntries() {
+ return ImmutableList.of(JAR_ENTRY);
+ }
+
+ @Override
+ protected String getExpectedOutputForReferenceImplementation() {
+ return EXPECTED_OUTPUT;
+ }
+
+ @Override
+ protected String getExpectedOutputForDesugaringImplementation() {
+ return StringUtils.lines("Got UnsupportedOperationException");
+ }
+
+ @Override
+ protected boolean getTestWithDesugaring() {
+ return true;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceByteFieldTest.java b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceByteFieldTest.java
new file mode 100644
index 0000000..a96b5a9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceByteFieldTest.java
@@ -0,0 +1,52 @@
+// 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.cf.varhandle;
+
+import com.android.tools.r8.examples.jdk9.VarHandle;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class VarHandleDesugaringInstanceByteFieldTest extends VarHandleDesugaringTestBase {
+
+ private static final String EXPECTED_OUTPUT =
+ StringUtils.lines("testGet", "0", "1", "2", "testCompareAndSet", "0", "1", "2", "3", "4");
+
+ private static final String MAIN_CLASS = VarHandle.InstanceByteField.typeName();
+ private static final String JAR_ENTRY = "varhandle/InstanceByteField.class";
+
+ @Override
+ protected String getMainClass() {
+ return MAIN_CLASS;
+ }
+
+ @Override
+ protected String getKeepRules() {
+ return "-keep class " + getMainClass() + "{ <fields>; }";
+ }
+
+ @Override
+ protected List<String> getJarEntries() {
+ return ImmutableList.of(JAR_ENTRY);
+ }
+
+ @Override
+ protected String getExpectedOutputForReferenceImplementation() {
+ return EXPECTED_OUTPUT;
+ }
+
+ @Override
+ protected String getExpectedOutputForDesugaringImplementation() {
+ return StringUtils.lines("Got UnsupportedOperationException");
+ }
+
+ @Override
+ protected boolean getTestWithDesugaring() {
+ return true;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceDoubleFieldTest.java b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceDoubleFieldTest.java
new file mode 100644
index 0000000..194f25f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceDoubleFieldTest.java
@@ -0,0 +1,53 @@
+// 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.cf.varhandle;
+
+import com.android.tools.r8.examples.jdk9.VarHandle;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class VarHandleDesugaringInstanceDoubleFieldTest extends VarHandleDesugaringTestBase {
+
+ private static final String EXPECTED_OUTPUT =
+ StringUtils.lines(
+ "testGet", "0.0", "1.0", "2.0", "testCompareAndSet", "0.0", "1.0", "2.0", "3.0", "4.0");
+
+ private static final String MAIN_CLASS = VarHandle.InstanceDoubleField.typeName();
+ private static final String JAR_ENTRY = "varhandle/InstanceDoubleField.class";
+
+ @Override
+ protected String getMainClass() {
+ return MAIN_CLASS;
+ }
+
+ @Override
+ protected String getKeepRules() {
+ return "-keep class " + getMainClass() + "{ <fields>; }";
+ }
+
+ @Override
+ protected List<String> getJarEntries() {
+ return ImmutableList.of(JAR_ENTRY);
+ }
+
+ @Override
+ protected String getExpectedOutputForReferenceImplementation() {
+ return EXPECTED_OUTPUT;
+ }
+
+ @Override
+ protected String getExpectedOutputForDesugaringImplementation() {
+ return StringUtils.lines("Got UnsupportedOperationException");
+ }
+
+ @Override
+ protected boolean getTestWithDesugaring() {
+ return true;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceFloatFieldTest.java b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceFloatFieldTest.java
new file mode 100644
index 0000000..457753b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceFloatFieldTest.java
@@ -0,0 +1,53 @@
+// 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.cf.varhandle;
+
+import com.android.tools.r8.examples.jdk9.VarHandle;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class VarHandleDesugaringInstanceFloatFieldTest extends VarHandleDesugaringTestBase {
+
+ private static final String EXPECTED_OUTPUT =
+ StringUtils.lines(
+ "testGet", "0.0", "1.0", "2.0", "testCompareAndSet", "0.0", "1.0", "2.0", "3.0", "4.0");
+
+ private static final String MAIN_CLASS = VarHandle.InstanceFloatField.typeName();
+ private static final String JAR_ENTRY = "varhandle/InstanceFloatField.class";
+
+ @Override
+ protected String getMainClass() {
+ return MAIN_CLASS;
+ }
+
+ @Override
+ protected String getKeepRules() {
+ return "-keep class " + getMainClass() + "{ <fields>; }";
+ }
+
+ @Override
+ protected List<String> getJarEntries() {
+ return ImmutableList.of(JAR_ENTRY);
+ }
+
+ @Override
+ protected String getExpectedOutputForReferenceImplementation() {
+ return EXPECTED_OUTPUT;
+ }
+
+ @Override
+ protected String getExpectedOutputForDesugaringImplementation() {
+ return StringUtils.lines("Got UnsupportedOperationException");
+ }
+
+ @Override
+ protected boolean getTestWithDesugaring() {
+ return true;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceShortFieldTest.java b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceShortFieldTest.java
new file mode 100644
index 0000000..a5947eb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceShortFieldTest.java
@@ -0,0 +1,52 @@
+// 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.cf.varhandle;
+
+import com.android.tools.r8.examples.jdk9.VarHandle;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class VarHandleDesugaringInstanceShortFieldTest extends VarHandleDesugaringTestBase {
+
+ private static final String EXPECTED_OUTPUT =
+ StringUtils.lines("testGet", "0", "1", "2", "testCompareAndSet", "0", "1", "2", "3", "4");
+
+ private static final String MAIN_CLASS = VarHandle.InstanceShortField.typeName();
+ private static final String JAR_ENTRY = "varhandle/InstanceShortField.class";
+
+ @Override
+ protected String getMainClass() {
+ return MAIN_CLASS;
+ }
+
+ @Override
+ protected String getKeepRules() {
+ return "-keep class " + getMainClass() + "{ <fields>; }";
+ }
+
+ @Override
+ protected List<String> getJarEntries() {
+ return ImmutableList.of(JAR_ENTRY);
+ }
+
+ @Override
+ protected String getExpectedOutputForReferenceImplementation() {
+ return EXPECTED_OUTPUT;
+ }
+
+ @Override
+ protected String getExpectedOutputForDesugaringImplementation() {
+ return StringUtils.lines("Got UnsupportedOperationException");
+ }
+
+ @Override
+ protected boolean getTestWithDesugaring() {
+ return true;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringTestBase.java b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringTestBase.java
index 6a73d66..bb4d99b 100644
--- a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringTestBase.java
+++ b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringTestBase.java
@@ -18,7 +18,6 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.examples.jdk9.VarHandle;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.references.MethodReference;
@@ -65,6 +64,10 @@
protected abstract String getExpectedOutputForReferenceImplementation();
+ protected String getExpectedOutputForDesugaringImplementation() {
+ return getExpectedOutputForReferenceImplementation();
+ }
+
protected String getExpectedOutputForArtImplementation() {
return getExpectedOutputForReferenceImplementation();
}
@@ -111,7 +114,7 @@
// forwarding of Unsafe.compareAndSwapObject.
MethodReference firstBackportFromDesugarVarHandle =
SyntheticItemsTestUtils.syntheticBackportWithForwardingMethod(
- Reference.classFromDescriptor(DexItemFactory.desugarVarHandleDescriptorString),
+ Reference.classFromDescriptor("Ljava/lang/invoke/VarHandle;"),
0,
Reference.method(
Reference.classFromDescriptor("Lsun/misc/Unsafe;"),
@@ -198,7 +201,7 @@
.getDexRuntimeVersion()
.isNewerThanOrEqual(Version.V13_0_0)
? getExpectedOutputForArtImplementation()
- : getExpectedOutputForReferenceImplementation()))
+ : getExpectedOutputForDesugaringImplementation()))
.inspect(this::inspect);
} else {
testForD8(parameters.getBackend())
@@ -220,6 +223,18 @@
// TODO(b/247076137: Also turn on VarHandle desugaring for R8 tests.
@Test
public void testR8() throws Throwable {
+ // TODO(b/247076137: The "default" VM is acting up on some tests - skip these as they will
+ // be fixed when VarHandle desugaring is enabled for R8.
+ if (parameters.isDexRuntime()
+ && parameters.asDexRuntime().getVersion().isEqualTo(Version.DEFAULT)
+ && parameters.getApiLevel().equals(AndroidApiLevel.B)
+ && (this instanceof VarHandleDesugaringInstanceBooleanFieldTest
+ || this instanceof VarHandleDesugaringInstanceByteFieldTest
+ || this instanceof VarHandleDesugaringInstanceShortFieldTest
+ || this instanceof VarHandleDesugaringInstanceFloatFieldTest
+ || this instanceof VarHandleDesugaringInstanceDoubleFieldTest)) {
+ return;
+ }
testForR8(parameters.getBackend())
.applyIf(
parameters.isDexRuntime(),
diff --git a/src/test/java/com/android/tools/r8/cfmethodgeneration/CodeGenerationBase.java b/src/test/java/com/android/tools/r8/cfmethodgeneration/CodeGenerationBase.java
index ee1a991..0f3c23b 100644
--- a/src/test/java/com/android/tools/r8/cfmethodgeneration/CodeGenerationBase.java
+++ b/src/test/java/com/android/tools/r8/cfmethodgeneration/CodeGenerationBase.java
@@ -20,9 +20,9 @@
public abstract class CodeGenerationBase extends TestBase {
private static final Path GOOGLE_FORMAT_DIR =
- Paths.get(ToolHelper.THIRD_PARTY_DIR, "google-java-format");
+ Paths.get(ToolHelper.THIRD_PARTY_DIR, "google", "google-java-format", "1.14.0");
private static final Path GOOGLE_FORMAT_JAR =
- GOOGLE_FORMAT_DIR.resolve("google-java-format-1.7-all-deps.jar");
+ GOOGLE_FORMAT_DIR.resolve("google-java-format-1.14.0-all-deps.jar");
protected final DexItemFactory factory = new DexItemFactory();
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/NullAssignmentToArrayArgEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/NullAssignmentToArrayArgEnumUnboxingTest.java
new file mode 100644
index 0000000..fa5450f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/NullAssignmentToArrayArgEnumUnboxingTest.java
@@ -0,0 +1,90 @@
+// 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.enumunboxing;
+
+import com.android.tools.r8.TestParameters;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class NullAssignmentToArrayArgEnumUnboxingTest extends EnumUnboxingTestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameter(1)
+ public boolean enumValueOptimization;
+
+ @Parameter(2)
+ public EnumKeepRules enumKeepRules;
+
+ @Parameters(name = "{0}, value opt.: {1}, keep: {2}")
+ public static List<Object[]> data() {
+ return enumUnboxingTestParameters();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
+ // We need to disable entirely inlining since not only the methods checkNotNull and
+ // contains should not be inlined, but also the synthetic method with the zero check
+ // replacing the checkNotNull method should not be inlined.
+ .addOptionsModification(opt -> opt.inlinerOptions().enableInlining = false)
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("false", "true", "npe", "npe");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(contains(MyEnum.A, new MyEnum[] {MyEnum.B, MyEnum.C}));
+ System.out.println(contains(MyEnum.B, new MyEnum[] {MyEnum.B, MyEnum.C}));
+ try {
+ System.out.println(contains(MyEnum.B, null));
+ } catch (NullPointerException npe) {
+ System.out.println("npe");
+ }
+ try {
+ System.out.println(contains(null, new MyEnum[] {MyEnum.B, MyEnum.C}));
+ } catch (NullPointerException npe) {
+ System.out.println("npe");
+ }
+ }
+
+ static void checkNotNull(Object o, String msg) {
+ if (o == null) {
+ throw new NullPointerException(msg);
+ }
+ }
+
+ static boolean contains(MyEnum e, MyEnum[] contents) {
+ checkNotNull(e, "elem");
+ checkNotNull(contents, "array");
+ for (MyEnum content : contents) {
+ if (content == e) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ enum MyEnum {
+ A,
+ B,
+ C;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/examples/jdk9/VarHandle.java b/src/test/java/com/android/tools/r8/examples/jdk9/VarHandle.java
index 620e01b..0cb3d7f 100644
--- a/src/test/java/com/android/tools/r8/examples/jdk9/VarHandle.java
+++ b/src/test/java/com/android/tools/r8/examples/jdk9/VarHandle.java
@@ -29,6 +29,21 @@
public static final JavaExampleClassProxy InstanceLongField =
new JavaExampleClassProxy(EXAMPLE_FILE, "varhandle/InstanceLongField");
+ public static final JavaExampleClassProxy InstanceBooleanField =
+ new JavaExampleClassProxy(EXAMPLE_FILE, "varhandle/InstanceBooleanField");
+
+ public static final JavaExampleClassProxy InstanceByteField =
+ new JavaExampleClassProxy(EXAMPLE_FILE, "varhandle/InstanceByteField");
+
+ public static final JavaExampleClassProxy InstanceShortField =
+ new JavaExampleClassProxy(EXAMPLE_FILE, "varhandle/InstanceShortField");
+
+ public static final JavaExampleClassProxy InstanceFloatField =
+ new JavaExampleClassProxy(EXAMPLE_FILE, "varhandle/InstanceFloatField");
+
+ public static final JavaExampleClassProxy InstanceDoubleField =
+ new JavaExampleClassProxy(EXAMPLE_FILE, "varhandle/InstanceDoubleField");
+
public static final JavaExampleClassProxy InstanceObjectField =
new JavaExampleClassProxy(EXAMPLE_FILE, "varhandle/InstanceObjectField");
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/varhandle/DesugarVarHandle.java b/src/test/java/com/android/tools/r8/ir/desugar/varhandle/DesugarVarHandle.java
index 30b3240..5780485 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/varhandle/DesugarVarHandle.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/varhandle/DesugarVarHandle.java
@@ -79,6 +79,14 @@
this.recv = recv;
Field field = recv.getDeclaredField(name);
this.type = field.getType();
+ if (type.isPrimitive() && type != int.class && type != long.class) {
+ throw new UnsupportedOperationException(
+ "Using a VarHandle for a field of type '"
+ + type.getName()
+ + "' requires native VarHandle support available from Android 13. "
+ + "VarHandle desugaring only supports primitive types int and long and "
+ + "reference types.");
+ }
this.offset = U.objectFieldOffset(recv.getDeclaredField(name));
}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/varhandle/GenerateVarHandleMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/varhandle/GenerateVarHandleMethods.java
index b72d827..369bc51 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/varhandle/GenerateVarHandleMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/varhandle/GenerateVarHandleMethods.java
@@ -128,7 +128,7 @@
DexType holderType = invoke.getMethod().getHolderType();
DexType rewrittenType = typeMap.getOrDefault(holderType, holderType);
String rewrittenName =
- rewrittenType == factory.desugarVarHandleType ? methodNameMap.apply(name) : name;
+ rewrittenType == factory.varHandleType ? methodNameMap.apply(name) : name;
if (rewrittenType != holderType) {
// TODO(b/261024278): If sharing this code also rewrite signature.
return new CfInvoke(
@@ -179,8 +179,11 @@
new InstructionTypeMapper(
ImmutableMap.of(
factory.createType(
+ "L" + DesugarMethodHandlesLookup.class.getTypeName().replace('.', '/') + ";"),
+ factory.lookupType,
+ factory.createType(
"L" + DesugarVarHandle.class.getTypeName().replace('.', '/') + ";"),
- factory.desugarVarHandleType,
+ factory.varHandleType,
factory.createType(
"L" + DesugarVarHandle.class.getTypeName().replace('.', '/') + "$UnsafeStub;"),
factory.unsafeType),
@@ -194,16 +197,16 @@
private DexEncodedMethod methodWithName(DexEncodedMethod method, String name) {
DexType holder = method.getHolderType();
- DexType desugarVarHandle = factory.desugarVarHandleType;
+ DexType varHandle = factory.varHandleType;
DexType desugarVarHandleStub =
factory.createType("L" + DesugarVarHandle.class.getTypeName().replace('.', '/') + ";");
// Map methods to be on the final DesugarVarHandle class.
if (holder == desugarVarHandleStub) {
- holder = desugarVarHandle;
+ holder = varHandle;
}
DexProto proto = method.getProto();
if (proto.getReturnType() == desugarVarHandleStub) {
- proto = factory.createProto(desugarVarHandle, proto.parameters);
+ proto = factory.createProto(varHandle, proto.parameters);
}
return DexEncodedMethod.syntheticBuilder(method)
.setMethod(factory.createMethod(holder, proto, factory.createString(name)))
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepAnnotationViaSuperTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepAnnotationViaSuperTest.java
new file mode 100644
index 0000000..5f2237d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepAnnotationViaSuperTest.java
@@ -0,0 +1,151 @@
+// 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;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.keepanno.annotations.KeepOption;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.keepanno.annotations.UsesReflection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class KeepAnnotationViaSuperTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("42");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build();
+ }
+
+ public KeepAnnotationViaSuperTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(getInputClasses())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testWithRuleExtraction() throws Exception {
+ List<String> rules = getExtractedKeepRules();
+ System.out.println(rules);
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(getInputClassesWithoutKeepAnnotations())
+ .addKeepRules(rules)
+ .addKeepMainRule(TestClass.class)
+ .addKeepRuntimeVisibleAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkOutput);
+ }
+
+ public List<Class<?>> getInputClasses() {
+ return ImmutableList.of(
+ TestClass.class,
+ Base.class,
+ SubA.class,
+ SubB.class,
+ SubC.class,
+ Anno.class,
+ UnusedAnno.class);
+ }
+
+ public List<byte[]> getInputClassesWithoutKeepAnnotations() throws Exception {
+ return KeepEdgeAnnotationsTest.getInputClassesWithoutKeepAnnotations(getInputClasses());
+ }
+
+ public List<String> getExtractedKeepRules() throws Exception {
+ List<Class<?>> classes = getInputClasses();
+ List<String> rules = new ArrayList<>();
+ for (Class<?> clazz : classes) {
+ rules.addAll(KeepEdgeAnnotationsTest.getKeepRulesForClass(clazz));
+ }
+ return rules;
+ }
+
+ private void checkOutput(CodeInspector inspector) {
+ assertThat(inspector.clazz(Base.class), isPresent());
+
+ ClassSubject classA = inspector.clazz(SubA.class);
+ assertThat(classA, isPresent());
+ assertThat(classA.annotation(Anno.class), isPresent());
+ assertThat(classA.annotation(UnusedAnno.class), isAbsent());
+
+ ClassSubject classB = inspector.clazz(SubB.class);
+ assertThat(classB, isPresent());
+ assertThat(classB.annotation(Anno.class), isPresent());
+
+ assertThat(inspector.clazz(SubC.class), isAbsent());
+ }
+
+ @Target({ElementType.TYPE})
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface Anno {
+ int value();
+ }
+
+ @Target({ElementType.TYPE})
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface UnusedAnno {
+ int value();
+ }
+
+ abstract static class Base {
+
+ @UsesReflection({
+ @KeepTarget(
+ extendsClassConstant = Base.class,
+ disallow = {KeepOption.ANNOTATION_REMOVAL})
+ })
+ public Base() {
+ Anno annotation = getClass().getAnnotation(Anno.class);
+ System.out.println(annotation.value());
+ }
+ }
+
+ @Anno(42)
+ @UnusedAnno(123)
+ static class SubA extends Base {}
+
+ @Anno(7)
+ static class SubB extends Base {}
+
+ // Unused.
+ @Anno(-1)
+ static class SubC extends Base {}
+
+ static class TestClass {
+
+ public static void main(String[] args) throws Exception {
+ Base b = System.nanoTime() > 0 ? new SubA() : new SubB();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepEdgeAnnotationsTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepEdgeAnnotationsTest.java
index 8743b48..f99f3bf 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepEdgeAnnotationsTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepEdgeAnnotationsTest.java
@@ -14,6 +14,7 @@
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;
import com.android.tools.r8.keepanno.annotations.KeepConstants.Edge;
import com.android.tools.r8.keepanno.asm.KeepEdgeReader;
import com.android.tools.r8.keepanno.asm.KeepEdgeWriter;
@@ -36,6 +37,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -138,6 +140,16 @@
});
}
+ public static List<byte[]> getInputClassesWithoutKeepAnnotations(Collection<Class<?>> classes)
+ throws Exception {
+ List<byte[]> transformed = new ArrayList<>(classes.size());
+ for (Class<?> clazz : classes) {
+ transformed.add(
+ transformer(clazz).removeAnnotations(KeepConstants::isKeepAnnotation).transform());
+ }
+ return transformed;
+ }
+
@Test
public void testAsmReader() throws Exception {
assumeTrue(parameters.isCfRuntime());
@@ -147,11 +159,7 @@
byte[] original = ToolHelper.getClassAsBytes(source);
// Strip out all the annotations to ensure they are actually added again.
byte[] stripped =
- transformer(source)
- .removeClassAnnotations()
- .removeMethodAnnotations()
- .removeFieldAnnotations()
- .transform();
+ getInputClassesWithoutKeepAnnotations(Collections.singletonList(source)).get(0);
// Manually add in the expected edges again.
byte[] readded =
transformer(stripped, clazz)
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepInvalidTargetTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepInvalidTargetTest.java
new file mode 100644
index 0000000..6f95aa6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepInvalidTargetTest.java
@@ -0,0 +1,123 @@
+// 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;
+
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.keepanno.annotations.KeepOption;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import com.android.tools.r8.keepanno.annotations.UsesReflection;
+import com.android.tools.r8.keepanno.ast.KeepEdgeException;
+import org.hamcrest.Matcher;
+import org.junit.Test;
+import org.junit.function.ThrowingRunnable;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class KeepInvalidTargetTest extends TestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public KeepInvalidTargetTest(TestParameters parameters) {
+ parameters.assertNoneRuntime();
+ }
+
+ private void assertThrowsWith(ThrowingRunnable fn, Matcher<String> matcher) {
+ try {
+ fn.run();
+ fail("Expected run to fail");
+ } catch (KeepEdgeException e) {
+ assertThat(e.getMessage(), matcher);
+ } catch (Throwable e) {
+ fail("Expected run to fail with KeepEdgeException");
+ }
+ }
+
+ @Test
+ public void testInvalidClassDecl() throws Exception {
+ assertThrowsWith(
+ () -> KeepEdgeAnnotationsTest.getKeepRulesForClass(MultipleClassDeclarations.class),
+ allOf(
+ containsString("Multiple declarations"),
+ containsString("className"),
+ containsString("classConstant")));
+ }
+
+ static class MultipleClassDeclarations {
+
+ @UsesReflection(@KeepTarget(className = "foo", classConstant = MultipleClassDeclarations.class))
+ public static void main(String[] args) throws Exception {
+ System.out.println("Hello, world");
+ }
+ }
+
+ @Test
+ public void testInvalidExtendsDecl() throws Exception {
+ assertThrowsWith(
+ () -> KeepEdgeAnnotationsTest.getKeepRulesForClass(MultipleExtendsDeclarations.class),
+ allOf(
+ containsString("Multiple declarations"),
+ containsString("extendsClassName"),
+ containsString("extendsClassConstant")));
+ }
+
+ static class MultipleExtendsDeclarations {
+
+ @UsesReflection(
+ @KeepTarget(
+ extendsClassName = "foo",
+ extendsClassConstant = MultipleClassDeclarations.class))
+ public static void main(String[] args) throws Exception {
+ System.out.println("Hello, world");
+ }
+ }
+
+ @Test
+ public void testInvalidMemberDecl() throws Exception {
+ assertThrowsWith(
+ () -> KeepEdgeAnnotationsTest.getKeepRulesForClass(MultipleMemberDeclarations.class),
+ allOf(containsString("field"), containsString("method")));
+ }
+
+ static class MultipleMemberDeclarations {
+
+ @UsesReflection(@KeepTarget(classConstant = A.class, methodName = "foo", fieldName = "bar"))
+ public static void main(String[] args) throws Exception {
+ System.out.println("Hello, world");
+ }
+ }
+
+ @Test
+ public void testInvalidOptionsDecl() throws Exception {
+ assertThrowsWith(
+ () -> KeepEdgeAnnotationsTest.getKeepRulesForClass(MultipleOptionDeclarations.class),
+ allOf(containsString("options"), containsString("allow"), containsString("disallow")));
+ }
+
+ static class MultipleOptionDeclarations {
+
+ @UsesReflection(
+ @KeepTarget(
+ classConstant = A.class,
+ allow = {KeepOption.OPTIMIZATION},
+ disallow = {KeepOption.SHRINKING}))
+ public static void main(String[] args) throws Exception {
+ System.out.println("Hello, world");
+ }
+ }
+
+ static class A {
+ // just a target.
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationTest.java
index f9d98e3..d692791 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationTest.java
@@ -65,12 +65,7 @@
}
public List<byte[]> getInputClassesWithoutAnnotations() throws Exception {
- List<Class<?>> classes = getInputClasses();
- List<byte[]> transformed = new ArrayList<>(classes.size());
- for (Class<?> clazz : classes) {
- transformed.add(transformer(clazz).removeAllAnnotations().transform());
- }
- return transformed;
+ return KeepEdgeAnnotationsTest.getInputClassesWithoutKeepAnnotations(getInputClasses());
}
public List<String> getExtractedKeepRules() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationWithAdditionalPreconditionTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationWithAdditionalPreconditionTest.java
index 732f2d2..33f2531 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationWithAdditionalPreconditionTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationWithAdditionalPreconditionTest.java
@@ -64,12 +64,7 @@
}
public List<byte[]> getInputClassesWithoutAnnotations() throws Exception {
- List<Class<?>> classes = getInputClasses();
- List<byte[]> transformed = new ArrayList<>(classes.size());
- for (Class<?> clazz : classes) {
- transformed.add(transformer(clazz).removeAllAnnotations().transform());
- }
- return transformed;
+ return KeepEdgeAnnotationsTest.getInputClassesWithoutKeepAnnotations(getInputClasses());
}
public List<String> getExtractedKeepRules() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionFieldAnnotationTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionFieldAnnotationTest.java
index b6cc560..bfc7fb5 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionFieldAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionFieldAnnotationTest.java
@@ -64,12 +64,7 @@
}
public List<byte[]> getInputClassesWithoutAnnotations() throws Exception {
- List<Class<?>> classes = getInputClasses();
- List<byte[]> transformed = new ArrayList<>(classes.size());
- for (Class<?> clazz : classes) {
- transformed.add(transformer(clazz).removeAllAnnotations().transform());
- }
- return transformed;
+ return KeepEdgeAnnotationsTest.getInputClassesWithoutKeepAnnotations(getInputClasses());
}
public List<String> getExtractedKeepRules() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionOnFieldTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionOnFieldTest.java
index 6739144..ee605b0 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionOnFieldTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionOnFieldTest.java
@@ -5,7 +5,9 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -50,7 +52,9 @@
@Test
public void testWithRuleExtraction() throws Exception {
List<String> rules = getExtractedKeepRules();
- System.out.println(rules);
+ assertEquals(1, rules.size());
+ assertThat(rules.get(0), containsString("context: " + descriptor(A.class) + "foo()V"));
+ assertThat(rules.get(0), containsString("description: Keep the\\nstring-valued fields"));
testForR8(parameters.getBackend())
.addProgramClassFileData(getInputClassesWithoutAnnotations())
.addKeepRules(rules)
@@ -66,12 +70,7 @@
}
public List<byte[]> getInputClassesWithoutAnnotations() throws Exception {
- List<Class<?>> classes = getInputClasses();
- List<byte[]> transformed = new ArrayList<>(classes.size());
- for (Class<?> clazz : classes) {
- transformed.add(transformer(clazz).removeAllAnnotations().transform());
- }
- return transformed;
+ return KeepEdgeAnnotationsTest.getInputClassesWithoutKeepAnnotations(getInputClasses());
}
public List<String> getExtractedKeepRules() throws Exception {
@@ -94,7 +93,13 @@
public String fieldA = "Hello, world";
public Integer fieldB = 42;
- @UsesReflection({@KeepTarget(classConstant = A.class, fieldType = "java.lang.String")})
+ @UsesReflection(
+ description = "Keep the\nstring-valued fields",
+ value = {
+ @KeepTarget(
+ className = "com.android.tools.r8.keepanno.KeepUsesReflectionOnFieldTest$A",
+ fieldType = "java.lang.String")
+ })
public void foo() throws Exception {
for (Field field : getClass().getDeclaredFields()) {
if (field.getType().equals(String.class)) {
diff --git a/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java b/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
index d9a6eaf..0386448 100644
--- a/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/ast/KeepEdgeAstTest.java
@@ -11,6 +11,8 @@
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 com.google.common.collect.ImmutableList;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -62,10 +64,10 @@
.build())
.build();
// Disallow will issue the full inverse of the known options, e.g., 'allowaccessmodification'.
- assertEquals(
- StringUtils.unixLines(
- "-keep,allowshrinking,allowobfuscation,allowaccessmodification class * { *; }"),
- extract(edge));
+ List<String> options =
+ ImmutableList.of("shrinking", "obfuscation", "accessmodification", "annotationremoval");
+ String allows = String.join(",allow", options);
+ assertEquals(StringUtils.unixLines("-keep,allow" + allows + " class * { *; }"), extract(edge));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeDuplicateMovedFieldTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeDuplicateMovedFieldTest.java
new file mode 100644
index 0000000..355dbf4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeDuplicateMovedFieldTest.java
@@ -0,0 +1,66 @@
+// 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.mappingcompose;
+
+import static com.android.tools.r8.mappingcompose.ComposeTestHelpers.doubleToSingleQuote;
+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.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.StringUtils;
+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 ComposeDuplicateMovedFieldTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ private static final String mappingFoo =
+ StringUtils.unixLines(
+ "# {'id':'com.android.tools.r8.mapping','version':'experimental'}",
+ "com.foo -> a:",
+ " int some.other.Class.f1 -> g1",
+ "com.bar -> b:",
+ " boolean f2 -> g2");
+ private static final String mappingBar =
+ StringUtils.unixLines(
+ "# {'id':'com.android.tools.r8.mapping','version':'experimental'}",
+ "b -> c:",
+ " int a.g1 -> h1",
+ " boolean g2 -> h2",
+ "com.baz -> d:",
+ " int a.g1 -> h1",
+ " boolean b.g2 -> h2");
+ private static final String mappingResult =
+ StringUtils.unixLines(
+ "# {'id':'com.android.tools.r8.mapping','version':'experimental'}",
+ "com.bar -> c:",
+ " boolean f2 -> h2",
+ " int some.other.Class.f1 -> h1",
+ "com.baz -> d:",
+ " boolean com.bar.f2 -> h2",
+ " int some.other.Class.f1 -> h1",
+ "com.foo -> a:");
+
+ @Test
+ public void testCompose() throws Exception {
+ ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
+ ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
+ String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
+ assertEquals(mappingResult, doubleToSingleQuote(composed));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeMovedFieldTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeMovedFieldTest.java
new file mode 100644
index 0000000..69b78c7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeMovedFieldTest.java
@@ -0,0 +1,59 @@
+// 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.mappingcompose;
+
+import static com.android.tools.r8.mappingcompose.ComposeTestHelpers.doubleToSingleQuote;
+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.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.StringUtils;
+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 ComposeMovedFieldTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ private static final String mappingFoo =
+ StringUtils.unixLines(
+ "# {'id':'com.android.tools.r8.mapping','version':'experimental'}",
+ "com.foo -> a:",
+ " int some.other.Class.f1 -> g1",
+ " boolean f2 -> g2");
+ private static final String mappingBar =
+ StringUtils.unixLines(
+ "# {'id':'com.android.tools.r8.mapping','version':'experimental'}",
+ "com.bar -> b:",
+ " int a.g1 -> h1",
+ " boolean a.g2 -> h2");
+ private static final String mappingResult =
+ StringUtils.unixLines(
+ "# {'id':'com.android.tools.r8.mapping','version':'experimental'}",
+ "com.bar -> b:",
+ " boolean com.foo.f2 -> h2",
+ " int some.other.Class.f1 -> h1",
+ "com.foo -> a:");
+
+ @Test
+ public void testCompose() throws Exception {
+ ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
+ ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
+ String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
+ assertEquals(mappingResult, doubleToSingleQuote(composed));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/DesugarPrivateLambdaRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/DesugarPrivateLambdaRetraceTest.java
new file mode 100644
index 0000000..01f9b37
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/retrace/DesugarPrivateLambdaRetraceTest.java
@@ -0,0 +1,89 @@
+// 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.naming.retrace;
+
+import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
+import com.android.tools.r8.naming.retrace.testclasses.DesugarInterfaceInstanceLambdaRetrace;
+import com.android.tools.r8.naming.retrace.testclasses.DesugarInterfaceInstanceLambdaRetrace.ConsumerDesugarLambda;
+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 DesugarPrivateLambdaRetraceTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ private final String fileName = "DesugarInterfaceInstanceLambdaRetrace.java";
+
+ public StackTrace expectedStackTrace =
+ StackTrace.builder()
+ .add(
+ StackTraceLine.builder()
+ .setClassName(typeName(ConsumerDesugarLambda.class))
+ .setMethodName("lambda$foo$0")
+ .setFileName(fileName)
+ .setLineNumber(16)
+ .build())
+ .add(
+ StackTraceLine.builder()
+ .setClassName(typeName(DesugarInterfaceInstanceLambdaRetrace.Main.class))
+ .setMethodName("method1")
+ .setFileName(fileName)
+ .setLineNumber(26)
+ .build())
+ .add(
+ StackTraceLine.builder()
+ .setClassName(typeName(ConsumerDesugarLambda.class))
+ .setMethodName("foo")
+ .setFileName(fileName)
+ .setLineNumber(13)
+ .build())
+ .add(
+ StackTraceLine.builder()
+ .setClassName(typeName(DesugarInterfaceInstanceLambdaRetrace.Main.class))
+ .setMethodName("main")
+ .setFileName(fileName)
+ .setLineNumber(30)
+ .build())
+ .build();
+
+ @Test
+ public void testJvm() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForJvm()
+ .addInnerClasses(DesugarInterfaceInstanceLambdaRetrace.class)
+ .run(TestRuntime.getDefaultCfRuntime(), DesugarInterfaceInstanceLambdaRetrace.Main.class)
+ .inspectStackTrace(stackTrace -> assertThat(stackTrace, isSame(expectedStackTrace)));
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ testForD8(parameters.getBackend())
+ .setMode(CompilationMode.DEBUG)
+ .addInnerClasses(DesugarInterfaceInstanceLambdaRetrace.class)
+ .setMinApi(parameters.getApiLevel())
+ .internalEnableMappingOutput()
+ .run(parameters.getRuntime(), DesugarInterfaceInstanceLambdaRetrace.Main.class)
+ .assertFailureWithErrorThatThrows(NullPointerException.class)
+ .inspectStackTrace(stackTrace -> assertThat(stackTrace, isSame(expectedStackTrace)));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
index 9e361e6..5b4dcb2 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
@@ -66,15 +66,6 @@
return retracedStackTraceLine.lineNumber > 0;
}
- private boolean filterSynthesizedBridgeMethod(StackTraceLine retracedStackTraceLine) {
- if (!haveSeenLines.add(retracedStackTraceLine)) {
- return false;
- }
- return !retracedStackTraceLine.className.contains("ResourceWrapper")
- || !retracedStackTraceLine.methodName.contains("foo")
- || retracedStackTraceLine.lineNumber != 0;
- }
-
@Test
public void testSourceFileAndLineNumberTable() throws Exception {
runTest(
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/testclasses/DesugarInterfaceInstanceLambdaRetrace.java b/src/test/java/com/android/tools/r8/naming/retrace/testclasses/DesugarInterfaceInstanceLambdaRetrace.java
new file mode 100644
index 0000000..301a994
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/retrace/testclasses/DesugarInterfaceInstanceLambdaRetrace.java
@@ -0,0 +1,33 @@
+// 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.naming.retrace.testclasses;
+
+public class DesugarInterfaceInstanceLambdaRetrace {
+
+ public interface ConsumerDesugarLambda {
+ String accept();
+
+ default void foo() {
+ Main.method1(
+ () -> {
+ if (System.currentTimeMillis() > 0) {
+ throw null;
+ }
+ return accept();
+ });
+ }
+ }
+
+ public static class Main {
+
+ public static void method1(ConsumerDesugarLambda iface) {
+ System.out.println(iface.accept());
+ }
+
+ public static void main(String[] args) {
+ ((ConsumerDesugarLambda) () -> "Hello World").foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java
deleted file mode 100644
index 7e6306d..0000000
--- a/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.naming.retraceproguard;
-
-import static com.android.tools.r8.naming.retraceproguard.StackTrace.isSameExceptForFileName;
-import static com.android.tools.r8.naming.retraceproguard.StackTrace.isSameExceptForFileNameAndLineNumber;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assume.assumeTrue;
-
-import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.R8TestBuilder;
-import com.android.tools.r8.R8TestCompileResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
-import com.android.tools.r8.naming.retraceproguard.StackTrace.StackTraceLine;
-import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.Box;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.google.common.collect.ImmutableList;
-import java.io.IOException;
-import java.util.Collection;
-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 VerticalClassMergingRetraceTest extends RetraceTestBase {
-
- @Parameters(name = "{0}, mode: {1}, compat: {2}")
- public static Collection<Object[]> data() {
- return buildParameters(
- getTestParameters()
- .withCfRuntimes()
- // Runtimes prior to 8 will emit stack trace lines as:
- // Exception in thread "main" java.lang.NullPointerException: throw with null exception
- // at com.android.tools.r8.naming.retraceproguard.a.b(SourceFile)
- // PG do not support retracing if no line number is specified.
- .withDexRuntimesStartingFromIncluding(Version.V8_1_0)
- .withAllApiLevels()
- .build(),
- CompilationMode.values(),
- BooleanUtils.values());
- }
-
- public VerticalClassMergingRetraceTest(
- TestParameters parameters, CompilationMode mode, boolean compat) {
- super(parameters, mode, compat);
- }
-
- @Override
- public void configure(R8TestBuilder builder) {
- builder.enableInliningAnnotations();
- }
-
- @Override
- public Collection<Class<?>> getClasses() {
- return ImmutableList.of(getMainClass(), ResourceWrapper.class, TintResources.class);
- }
-
- @Override
- public Class<?> getMainClass() {
- return MainApp.class;
- }
-
- private int expectedActualStackTraceHeight() {
- // In RELEASE mode, a synthetic bridge will be added by vertical class merger.
- int height = mode == CompilationMode.RELEASE ? 3 : 2;
- if (parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isDalvik()) {
- // Dalvik places a stack trace line in the bottom.
- height += 1;
- }
- return height;
- }
-
- private boolean filterSynthesizedMethod(
- StackTraceLine retracedStackTraceLine, MethodSubject syntheticMethod) {
- if (syntheticMethod.isPresent()) {
- String qualifiedMethodName =
- retracedStackTraceLine.className + "." + retracedStackTraceLine.methodName;
- return !qualifiedMethodName.equals(syntheticMethod.getOriginalName())
- || retracedStackTraceLine.lineNumber > 0;
- }
- return true;
- }
-
- @Test
- public void testSourceFileAndLineNumberTable() throws Exception {
- Box<MethodSubject> syntheticMethod = new Box<>();
- runTest(
- ImmutableList.of("-keepattributes SourceFile,LineNumberTable"),
- (StackTrace actualStackTrace, StackTrace retracedStackTrace) -> {
- // Even when SourceFile is present retrace replaces the file name in the stack trace.
- StackTrace reprocessedStackTrace =
- retracedStackTrace.filter(
- stackTraceLine -> filterSynthesizedMethod(stackTraceLine, syntheticMethod.get()));
- assertThat(
- reprocessedStackTrace.filter(this::isNotDalvikNativeStartMethod),
- isSameExceptForFileName(
- expectedStackTrace.filter(this::isNotDalvikNativeStartMethod)));
- assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size());
- },
- compileResult -> setSyntheticMethod(compileResult, syntheticMethod));
- }
-
- @Test
- public void testLineNumberTableOnly() throws Exception {
- assumeTrue(compat);
- assumeTrue(parameters.isDexRuntime());
- Box<MethodSubject> syntheticMethod = new Box<>();
- runTest(
- ImmutableList.of("-keepattributes LineNumberTable"),
- (StackTrace actualStackTrace, StackTrace retracedStackTrace) -> {
- StackTrace reprocessedStackTrace =
- retracedStackTrace.filter(
- stackTraceLine -> filterSynthesizedMethod(stackTraceLine, syntheticMethod.get()));
- assertThat(
- reprocessedStackTrace.filter(this::isNotDalvikNativeStartMethod),
- isSameExceptForFileName(
- expectedStackTrace.filter(this::isNotDalvikNativeStartMethod)));
- assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size());
- },
- compileResult -> setSyntheticMethod(compileResult, syntheticMethod));
- }
-
- private void setSyntheticMethod(
- R8TestCompileResult compileResult, Box<MethodSubject> syntheticMethod) throws IOException {
- compileResult.inspect(
- inspector -> {
- ClassSubject tintResourcesClassSubject = inspector.clazz(TintResources.class);
- MethodSubject uniqueSyntheticMethod =
- tintResourcesClassSubject.uniqueMethodThatMatches(
- method -> method.getAccessFlags().isSynthetic());
- assertThat(uniqueSyntheticMethod, onlyIf(mode == CompilationMode.RELEASE, isPresent()));
- syntheticMethod.set(uniqueSyntheticMethod);
- });
- }
-}
-
-class ResourceWrapper {
- // Will be merged down, and represented as:
- // java.lang.String ...ResourceWrapper.foo() -> a
- @NeverInline
- String foo(boolean doThrow) {
- if (doThrow) {
- throw null;
- }
- return System.currentTimeMillis() > 0 ? "arg" : null;
- }
-}
-
-class TintResources extends ResourceWrapper {}
-
-class MainApp {
- public static void main(String[] args) {
- TintResources t = new TintResources();
- boolean doThrow = System.currentTimeMillis() > 0;
- System.out.println(t.foo(doThrow));
- }
-}
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 1974bcd..7742119 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -1463,32 +1463,51 @@
});
}
- public ClassFileTransformer removeAllAnnotations() {
- return removeClassAnnotations().removeMethodAnnotations().removeFieldAnnotations();
+ public interface AnnotationPredicate {
+ boolean test(String descriptor, boolean visible);
+
+ static AnnotationPredicate any() {
+ return (descriptor, visible) -> true;
+ }
}
- public ClassFileTransformer removeClassAnnotations() {
+ public ClassFileTransformer removeAllAnnotations() {
+ return removeAnnotations(AnnotationPredicate.any());
+ }
+
+ public ClassFileTransformer removeAnnotations(AnnotationPredicate predicate) {
+ return removeClassAnnotations(predicate)
+ .removeMethodAnnotations(predicate)
+ .removeFieldAnnotations(predicate);
+ }
+
+ public ClassFileTransformer removeClassAnnotations(AnnotationPredicate predicate) {
return addClassTransformer(
new ClassTransformer() {
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
- // Ignore all input annotations.
- return null;
+ if (predicate.test(descriptor, visible)) {
+ return null;
+ }
+ return super.visitAnnotation(descriptor, visible);
}
});
}
- public ClassFileTransformer removeMethodAnnotations() {
+ public ClassFileTransformer removeMethodAnnotations(AnnotationPredicate predicate) {
return addMethodTransformer(
new MethodTransformer() {
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
- return null;
+ if (predicate.test(descriptor, visible)) {
+ return null;
+ }
+ return super.visitAnnotation(descriptor, visible);
}
});
}
- public ClassFileTransformer removeFieldAnnotations() {
+ public ClassFileTransformer removeFieldAnnotations(AnnotationPredicate predicate) {
return addClassTransformer(
new ClassTransformer() {
@Override
@@ -1498,7 +1517,10 @@
return new FieldVisitor(ASM_VERSION, fv) {
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
- return null;
+ if (predicate.test(descriptor, visible)) {
+ return null;
+ }
+ return super.visitAnnotation(descriptor, visible);
}
};
}
diff --git a/tools/asmifier.py b/tools/asmifier.py
index ae7d1e6..82b90af 100755
--- a/tools/asmifier.py
+++ b/tools/asmifier.py
@@ -10,7 +10,7 @@
import sys
import utils
-ASM_VERSION = '9.3'
+ASM_VERSION = '9.4'
ASM_JAR = 'asm-' + ASM_VERSION + '.jar'
ASM_UTIL_JAR = 'asm-util-' + ASM_VERSION + '.jar'