Merge commit '254a39d21274c47cdaed865ec0a5f0bb7ba51896' into dev-release
diff --git a/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java b/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java
index b02dc00..3ac81cd 100644
--- a/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java
+++ b/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java
@@ -28,6 +28,7 @@
"j$/nio/file/spi/FileTypeDetector",
"j$/nio/file/Path",
"j$/nio/file/WatchEvent",
+ "j$/nio/file/WatchEvent$Kind",
"j$/nio/file/OpenOption",
"j$/nio/file/attribute/FileAttribute");
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 6d66d0a..01c493f 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
@@ -31,5 +31,11 @@
String methodName() default "";
+ String methodReturnType() default "";
+
+ String[] methodParameters() default {""};
+
String fieldName() default "";
+
+ String fieldType() 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 260c31c..0018dfc 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
@@ -37,8 +37,23 @@
// Implicit hidden item which is "super type" of Condition and Target.
public static final class Item {
public static final String classConstant = "classConstant";
+
public static final String methodName = "methodName";
+ public static final String methodReturnType = "methodReturnType";
+ public static final String methodParameters = "methodParameters";
+
public static final String fieldName = "fieldName";
+ public static final String fieldType = "fieldType";
+
+ // 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 methodNameDefaultValue = "";
+ public static final String methodReturnTypeDefaultValue = "";
+ public static final String[] methodParametersDefaultValue = new String[] {""};
+
+ public static final String fieldNameDefaultValue = "";
+ public static final String fieldTypeDefaultValue = "";
}
public static final class Condition {
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 f87e32c..73559d6 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
@@ -8,6 +8,20 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+/**
+ * A target for a keep edge.
+ *
+ * <p>The target denotes a keep item along with options for what to keep:
+ *
+ * <ul>
+ * <li>a class, or pattern on classes;
+ * <li>a method, or pattern on methods; or
+ * <li>a field, or pattern on fields.
+ * </ul>
+ *
+ * <p>The structure of a target item is the same as for a condition item but has the additional keep
+ * options.
+ */
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface KeepTarget {
@@ -17,5 +31,11 @@
String methodName() default "";
+ String methodReturnType() default "";
+
+ String[] methodParameters() default {""};
+
String fieldName() default "";
+
+ String fieldType() 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 3545a25..672a99e7 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
@@ -14,15 +14,23 @@
import com.android.tools.r8.keepanno.ast.KeepEdgeException;
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.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.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.HashSet;
+import java.util.List;
import java.util.Set;
+import java.util.function.Consumer;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
@@ -115,14 +123,24 @@
}
private KeepItemPattern createItemContext() {
- Type returnType = Type.getReturnType(methodDescriptor);
+ String returnTypeDescriptor = Type.getReturnType(methodDescriptor).getDescriptor();
Type[] argumentTypes = Type.getArgumentTypes(methodDescriptor);
- // TODO(b/248408342): Defaults are "any", support setting actual return type and params.
+ KeepMethodParametersPattern.Builder builder = KeepMethodParametersPattern.builder();
+ for (Type type : argumentTypes) {
+ builder.addParameterTypePattern(KeepTypePattern.fromDescriptor(type.getDescriptor()));
+ }
+ KeepMethodReturnTypePattern returnTypePattern =
+ "V".equals(returnTypeDescriptor)
+ ? KeepMethodReturnTypePattern.voidType()
+ : KeepMethodReturnTypePattern.fromType(
+ KeepTypePattern.fromDescriptor(returnTypeDescriptor));
return KeepItemPattern.builder()
.setClassPattern(KeepQualifiedClassNamePattern.exact(className))
.setMemberPattern(
KeepMethodPattern.builder()
.setNamePattern(KeepMethodNamePattern.exact(methodName))
+ .setReturnTypePattern(returnTypePattern)
+ .setParametersPattern(builder.build())
.build())
.build();
}
@@ -159,12 +177,14 @@
}
private KeepItemPattern createItemContext() {
- // TODO(b/248408342): Default type is "any", support setting actual field type.
+ KeepFieldTypePattern typePattern =
+ KeepFieldTypePattern.fromType(KeepTypePattern.fromDescriptor(fieldDescriptor));
return KeepItemPattern.builder()
.setClassPattern(KeepQualifiedClassNamePattern.exact(className))
.setMemberPattern(
KeepFieldPattern.builder()
.setNamePattern(KeepFieldNamePattern.exact(fieldName))
+ .setTypePattern(typePattern)
.build())
.build();
}
@@ -320,13 +340,33 @@
private final Parent<KeepItemPattern> parent;
private KeepQualifiedClassNamePattern classNamePattern = null;
- private KeepMethodNamePattern methodName = null;
- private KeepFieldNamePattern fieldName = null;
+ private KeepMethodPattern.Builder lazyMethodBuilder = null;
+ private KeepFieldPattern.Builder lazyFieldBuilder = null;
public KeepItemVisitorBase(Parent<KeepItemPattern> parent) {
this.parent = parent;
}
+ 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;
+ }
+
+ 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;
+ }
+
@Override
public void visit(String name, Object value) {
if (name.equals(Item.classConstant) && value instanceof Type) {
@@ -334,36 +374,100 @@
return;
}
if (name.equals(Item.methodName) && value instanceof String) {
- methodName = KeepMethodNamePattern.exact((String) value);
+ String methodName = (String) value;
+ if (!Item.methodNameDefaultValue.equals(methodName)) {
+ methodBuilder().setNamePattern(KeepMethodNamePattern.exact(methodName));
+ }
+ return;
+ }
+ if (name.equals(Item.methodReturnType) && value instanceof String) {
+ String returnType = (String) value;
+ if (!Item.methodReturnTypeDefaultValue.equals(returnType)) {
+ methodBuilder()
+ .setReturnTypePattern(KeepEdgeReaderUtils.methodReturnTypeFromString(returnType));
+ }
return;
}
if (name.equals(Item.fieldName) && value instanceof String) {
- fieldName = KeepFieldNamePattern.exact((String) value);
+ 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);
}
@Override
+ public AnnotationVisitor visitArray(String name) {
+ if (name.equals(Item.methodParameters)) {
+ return new StringArrayVisitor(
+ params -> {
+ if (Arrays.asList(Item.methodParametersDefaultValue).equals(params)) {
+ return;
+ }
+ KeepMethodParametersPattern.Builder builder = KeepMethodParametersPattern.builder();
+ for (String param : params) {
+ builder.addParameterTypePattern(KeepEdgeReaderUtils.typePatternFromString(param));
+ }
+ methodBuilder().setParametersPattern(builder.build());
+ });
+ }
+ return super.visitArray(name);
+ }
+
+ @Override
public void visitEnd() {
+ assert lazyMethodBuilder == null || lazyFieldBuilder == null;
Builder itemBuilder = KeepItemPattern.builder();
if (classNamePattern != null) {
itemBuilder.setClassPattern(classNamePattern);
}
- if (methodName != null && fieldName != null) {
- throw new KeepEdgeException("Cannot define both a field and a method pattern.");
+ if (lazyMethodBuilder != null) {
+ itemBuilder.setMemberPattern(lazyMethodBuilder.build());
}
- if (methodName != null) {
- itemBuilder.setMemberPattern(
- KeepMethodPattern.builder().setNamePattern(methodName).build());
- }
- if (fieldName != null) {
- itemBuilder.setMemberPattern(KeepFieldPattern.builder().setNamePattern(fieldName).build());
+ if (lazyFieldBuilder != null) {
+ itemBuilder.setMemberPattern(lazyFieldBuilder.build());
}
parent.accept(itemBuilder.build());
}
}
+ private static class StringArrayVisitor extends AnnotationVisitorBase {
+
+ private final Consumer<List<String>> fn;
+ private final List<String> strings = new ArrayList<>();
+
+ public StringArrayVisitor(Consumer<List<String>> fn) {
+ this.fn = fn;
+ }
+
+ @Override
+ public void visit(String name, Object value) {
+ if (value instanceof String) {
+ strings.add((String) value);
+ } else {
+ super.visit(name, value);
+ }
+ }
+
+ @Override
+ public void visitEnd() {
+ super.visitEnd();
+ fn.accept(strings);
+ }
+ }
+
private static class KeepTargetVisitor extends KeepItemVisitorBase {
public KeepTargetVisitor(Parent<KeepTarget> parent) {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReaderUtils.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReaderUtils.java
new file mode 100644
index 0000000..e18bf1e
--- /dev/null
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReaderUtils.java
@@ -0,0 +1,69 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.keepanno.asm;
+
+import com.android.tools.r8.keepanno.ast.KeepEdgeException;
+import com.android.tools.r8.keepanno.ast.KeepMethodReturnTypePattern;
+import com.android.tools.r8.keepanno.ast.KeepTypePattern;
+
+/**
+ * Utilities for mapping the syntax used in annotations to the keep-edge AST.
+ *
+ * <p>The AST explicitly avoids interpreting type strings as they are potentially ambiguous. These
+ * utilities define the mappings from such syntax strings into the AST.
+ */
+public class KeepEdgeReaderUtils {
+
+ public static KeepTypePattern typePatternFromString(String string) {
+ if (string.equals("<any>")) {
+ return KeepTypePattern.any();
+ }
+ return KeepTypePattern.fromDescriptor(javaTypeToDescriptor(string));
+ }
+
+ public static String javaTypeToDescriptor(String type) {
+ switch (type) {
+ case "boolean":
+ return "Z";
+ case "byte":
+ return "B";
+ case "short":
+ return "S";
+ case "int":
+ return "I";
+ case "long":
+ return "J";
+ case "float":
+ return "F";
+ case "double":
+ return "D";
+ default:
+ {
+ StringBuilder builder = new StringBuilder(type.length());
+ int i = type.length() - 1;
+ while (type.charAt(i) == ']') {
+ if (type.charAt(--i) != '[') {
+ throw new KeepEdgeException("Invalid type: " + type);
+ }
+ builder.append('[');
+ --i;
+ }
+ builder.append('L');
+ for (int j = 0; j <= i; j++) {
+ char c = type.charAt(j);
+ builder.append(c == '.' ? '/' : c);
+ }
+ builder.append(';');
+ return builder.toString();
+ }
+ }
+ }
+
+ public static KeepMethodReturnTypePattern methodReturnTypeFromString(String returnType) {
+ if ("void".equals(returnType)) {
+ return KeepMethodReturnTypePattern.voidType();
+ }
+ return KeepMethodReturnTypePattern.fromType(typePatternFromString(returnType));
+ }
+}
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 b2da25a..e1d6d4e 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
@@ -33,7 +33,7 @@
* ::= any
* | class QUALIFIED_CLASS_NAME_PATTERN extends EXTENDS_PATTERN { MEMBER_PATTERN }
*
- * TYPE_PATTERN ::= any
+ * TYPE_PATTERN ::= any | exact type-descriptor
* PACKAGE_PATTERN ::= any | exact package-name
* QUALIFIED_CLASS_NAME_PATTERN ::= any | PACKAGE_PATTERN | UNQUALIFIED_CLASS_NAME_PATTERN
* UNQUALIFIED_CLASS_NAME_PATTERN ::= any | exact simple-class-name
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldPattern.java
index 7137033..1466fc1 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldPattern.java
@@ -14,7 +14,7 @@
public static class Builder {
private KeepFieldAccessPattern accessPattern = KeepFieldAccessPattern.any();
- private KeepFieldNamePattern namePattern = null;
+ private KeepFieldNamePattern namePattern = KeepFieldNamePattern.any();
private KeepFieldTypePattern typePattern = KeepFieldTypePattern.any();
private Builder() {}
@@ -39,9 +39,6 @@
}
public KeepFieldPattern build() {
- if (namePattern == null) {
- throw new KeepEdgeException("Field pattern must declare a name pattern");
- }
return new KeepFieldPattern(accessPattern, namePattern, typePattern);
}
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldTypePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldTypePattern.java
index beaaa55..3965699 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldTypePattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepFieldTypePattern.java
@@ -9,6 +9,10 @@
return SomeType.ANY_TYPE_INSTANCE;
}
+ public static KeepFieldTypePattern fromType(KeepTypePattern typePattern) {
+ return typePattern.isAny() ? any() : new SomeType(typePattern);
+ }
+
public boolean isAny() {
return isType() && asType().isAny();
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodParametersPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodParametersPattern.java
index 29442ff..73f8669 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodParametersPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodParametersPattern.java
@@ -3,11 +3,16 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.keepanno.ast;
+import com.google.common.collect.ImmutableList;
import java.util.Collections;
import java.util.List;
public abstract class KeepMethodParametersPattern {
+ public static Builder builder() {
+ return new Builder();
+ }
+
public static KeepMethodParametersPattern any() {
return Any.getInstance();
}
@@ -30,6 +35,25 @@
return null;
}
+ public static class Builder {
+ ImmutableList.Builder<KeepTypePattern> parameterPatterns = ImmutableList.builder();
+
+ private Builder() {}
+
+ public Builder addParameterTypePattern(KeepTypePattern typePattern) {
+ parameterPatterns.add(typePattern);
+ return this;
+ }
+
+ public KeepMethodParametersPattern build() {
+ List<KeepTypePattern> list = parameterPatterns.build();
+ if (list.isEmpty()) {
+ return Some.EMPTY_INSTANCE;
+ }
+ return new Some(list);
+ }
+ }
+
private static class Some extends KeepMethodParametersPattern {
private static final Some EMPTY_INSTANCE = new Some(Collections.emptyList());
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java
index e00fedd..f84a4b1 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodPattern.java
@@ -35,11 +35,15 @@
return self();
}
- public Builder setReturnTypeVoid() {
- returnTypePattern = KeepMethodReturnTypePattern.voidType();
+ public Builder setReturnTypePattern(KeepMethodReturnTypePattern returnTypePattern) {
+ this.returnTypePattern = returnTypePattern;
return self();
}
+ public Builder setReturnTypeVoid() {
+ return setReturnTypePattern(KeepMethodReturnTypePattern.voidType());
+ }
+
public Builder setParametersPattern(KeepMethodParametersPattern parametersPattern) {
this.parametersPattern = parametersPattern;
return self();
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodReturnTypePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodReturnTypePattern.java
index ccfc183..807b1bc 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodReturnTypePattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepMethodReturnTypePattern.java
@@ -14,6 +14,10 @@
return VoidType.getInstance();
}
+ public static KeepMethodReturnTypePattern fromType(KeepTypePattern typePattern) {
+ return typePattern.isAny() ? any() : new SomeType(typePattern);
+ }
+
public boolean isAny() {
return isType() && asType().isAny();
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTypePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTypePattern.java
index c7c24b5..872ad07 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTypePattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepTypePattern.java
@@ -9,7 +9,57 @@
return Any.getInstance();
}
+ public static KeepTypePattern fromDescriptor(String typeDescriptor) {
+ return new Some(typeDescriptor);
+ }
+
+ public boolean isAny() {
+ return false;
+ }
+
+ public String getDescriptor() {
+ return null;
+ }
+
+ private static class Some extends KeepTypePattern {
+
+ private final String descriptor;
+
+ private Some(String descriptor) {
+ assert descriptor != null;
+ this.descriptor = descriptor;
+ }
+
+ @Override
+ public String getDescriptor() {
+ return descriptor;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Some some = (Some) o;
+ return descriptor.equals(some.descriptor);
+ }
+
+ @Override
+ public int hashCode() {
+ return descriptor.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return descriptor;
+ }
+ }
+
private static class Any extends KeepTypePattern {
+
private static final Any INSTANCE = new Any();
public static Any getInstance() {
@@ -33,9 +83,7 @@
@Override
public String toString() {
- return "*";
+ return "<any>";
}
}
-
- public abstract boolean isAny();
}
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 6781a22..286c07a 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
@@ -5,6 +5,7 @@
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.KeepFieldAccessPattern;
import com.android.tools.r8.keepanno.ast.KeepFieldNamePattern;
import com.android.tools.r8.keepanno.ast.KeepFieldPattern;
@@ -157,7 +158,7 @@
.append('(')
.append(
parametersPattern.asList().stream()
- .map(Object::toString)
+ .map(KeepRuleExtractor::getTypePatternString)
.collect(Collectors.joining(", ")))
.append(')');
}
@@ -185,10 +186,7 @@
}
private static StringBuilder printType(StringBuilder builder, KeepTypePattern typePattern) {
- if (typePattern.isAny()) {
- return builder.append("***");
- }
- throw new Unimplemented();
+ return builder.append(getTypePatternString(typePattern));
}
private static StringBuilder printAccess(
@@ -256,6 +254,70 @@
}
}
+ private static String getTypePatternString(KeepTypePattern typePattern) {
+ if (typePattern.isAny()) {
+ return "***";
+ }
+ return descriptorToJavaType(typePattern.getDescriptor());
+ }
+
+ private static String descriptorToJavaType(String descriptor) {
+ if (descriptor.isEmpty()) {
+ throw new KeepEdgeException("Invalid empty type descriptor");
+ }
+ if (descriptor.length() == 1) {
+ return primitiveDescriptorToJavaType(descriptor.charAt(0));
+ }
+ if (descriptor.charAt(0) == '[') {
+ return arrayDescriptorToJavaType(descriptor);
+ }
+ return classDescriptorToJavaType(descriptor);
+ }
+
+ private static String primitiveDescriptorToJavaType(char descriptor) {
+ switch (descriptor) {
+ case 'Z':
+ return "boolean";
+ case 'B':
+ return "byte";
+ case 'S':
+ return "short";
+ case 'I':
+ return "int";
+ case 'J':
+ return "long";
+ case 'F':
+ return "float";
+ case 'D':
+ return "double";
+ default:
+ throw new KeepEdgeException("Invalid primitive descriptor: " + descriptor);
+ }
+ }
+
+ private static String classDescriptorToJavaType(String descriptor) {
+ int last = descriptor.length() - 1;
+ if (descriptor.charAt(0) != 'L' || descriptor.charAt(last) != ';') {
+ throw new KeepEdgeException("Invalid class descriptor: " + descriptor);
+ }
+ return descriptor.substring(1, last).replace('/', '.');
+ }
+
+ private static String arrayDescriptorToJavaType(String descriptor) {
+ for (int i = 0; i < descriptor.length(); i++) {
+ char c = descriptor.charAt(i);
+ if (c != '[') {
+ StringBuilder builder = new StringBuilder();
+ builder.append(descriptorToJavaType(descriptor.substring(i)));
+ for (int j = 0; j < i; j++) {
+ builder.append("[]");
+ }
+ return builder.toString();
+ }
+ }
+ throw new KeepEdgeException("Invalid array descriptor: " + descriptor);
+ }
+
private static class ItemRule {
private final KeepTarget target;
private final KeepOptions options;
diff --git a/src/library_desugar/java/j$/nio/file/StandardWatchEventKinds.java b/src/library_desugar/java/j$/nio/file/StandardWatchEventKinds.java
new file mode 100644
index 0000000..639b2c8
--- /dev/null
+++ b/src/library_desugar/java/j$/nio/file/StandardWatchEventKinds.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package j$.nio.file;
+
+public class StandardWatchEventKinds implements j$.nio.file.WatchEvent.Kind {
+
+ public static final j$.nio.file.WatchEvent.Kind ENTRY_CREATE = null;
+ public static final j$.nio.file.WatchEvent.Kind ENTRY_DELETE = null;
+ public static final j$.nio.file.WatchEvent.Kind ENTRY_MODIFY = null;
+ public static final j$.nio.file.WatchEvent.Kind OVERFLOW = null;
+}
diff --git a/src/library_desugar/java/j$/nio/file/WatchEvent.java b/src/library_desugar/java/j$/nio/file/WatchEvent.java
index fd977e9..41afd63 100644
--- a/src/library_desugar/java/j$/nio/file/WatchEvent.java
+++ b/src/library_desugar/java/j$/nio/file/WatchEvent.java
@@ -13,4 +13,14 @@
public static j$.nio.file.WatchEvent<?> wrap_convert(java.nio.file.WatchEvent<?> option) {
return null;
}
+
+ public static java.nio.file.WatchEvent.Kind wrap_convert(j$.nio.file.WatchEvent.Kind kind) {
+ return null;
+ }
+
+ public static j$.nio.file.WatchEvent.Kind wrap_convert(java.nio.file.WatchEvent.Kind kind) {
+ return null;
+ }
+
+ public static interface Kind {}
}
diff --git a/src/library_desugar/java/java/nio/file/WatchEventKindConversions.java b/src/library_desugar/java/java/nio/file/WatchEventKindConversions.java
new file mode 100644
index 0000000..b91a8cf
--- /dev/null
+++ b/src/library_desugar/java/java/nio/file/WatchEventKindConversions.java
@@ -0,0 +1,51 @@
+// 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 java.nio.file;
+
+/**
+ * The class java.nio.file.StandardWatchEventKinds.ENTRY_CREATE is final so it cannot be wrapped,
+ * but effectively it is used only for its 4 static fields, similarly to an enum while not being an
+ * enum. We convert them explicitely here when converting j$.nio.file.WatchEvent$Kind.
+ */
+public class WatchEventKindConversions {
+
+ public static j$.nio.file.WatchEvent.Kind convert(java.nio.file.WatchEvent.Kind kind) {
+ if (kind == null) {
+ return null;
+ }
+ if (kind == java.nio.file.StandardWatchEventKinds.ENTRY_CREATE) {
+ return j$.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
+ }
+ if (kind == java.nio.file.StandardWatchEventKinds.ENTRY_DELETE) {
+ return j$.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
+ }
+ if (kind == java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY) {
+ return j$.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
+ }
+ if (kind == java.nio.file.StandardWatchEventKinds.OVERFLOW) {
+ return j$.nio.file.StandardWatchEventKinds.OVERFLOW;
+ }
+ return j$.nio.file.WatchEvent.wrap_convert(kind);
+ }
+
+ public static java.nio.file.WatchEvent.Kind convert(j$.nio.file.WatchEvent.Kind kind) {
+ if (kind == null) {
+ return null;
+ }
+ if (kind == j$.nio.file.StandardWatchEventKinds.ENTRY_CREATE) {
+ return java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
+ }
+ if (kind == j$.nio.file.StandardWatchEventKinds.ENTRY_DELETE) {
+ return java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
+ }
+ if (kind == j$.nio.file.StandardWatchEventKinds.ENTRY_MODIFY) {
+ return java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
+ }
+ if (kind == j$.nio.file.StandardWatchEventKinds.OVERFLOW) {
+ return java.nio.file.StandardWatchEventKinds.OVERFLOW;
+ }
+ return j$.nio.file.WatchEvent.wrap_convert(kind);
+ }
+}
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
index 4bcf1a3..e963121 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
@@ -283,7 +283,8 @@
},
"custom_conversion": {
"java.nio.file.attribute.FileTime": "java.nio.file.attribute.FileAttributeConversions",
- "java.nio.file.attribute.FileAttribute": "java.nio.file.attribute.FileAttributeConversions"
+ "java.nio.file.attribute.FileAttribute": "java.nio.file.attribute.FileAttributeConversions",
+ "java.nio.file.WatchEvent$Kind": "java.nio.file.WatchEventKindConversions"
}
},
{
@@ -502,35 +503,11 @@
"sun.misc.DesugarUnsafe": {
"jdk.internal.misc.Unsafe" : "j$.sun.misc.DesugarUnsafe"
},
- "java.nio.file.attribute.": {
- "j$.nio.file.attribute.": "java.nio.file.attribute."
- },
- "java.nio.file.OpenOption": {
- "j$.nio.file.OpenOption": "java.nio.file.OpenOption"
- },
- "java.nio.file.StandardOpenOption": {
- "j$.nio.file.StandardOpenOption": "java.nio.file.StandardOpenOption"
- },
- "java.nio.file.LinkOption": {
- "j$.nio.file.LinkOption": "java.nio.file.LinkOption"
- },
- "java.nio.file.Files": {
- "j$.nio.file.Files": "java.nio.file.Files"
- },
- "java.nio.file.FileSystem": {
- "j$.nio.file.FileSystem": "java.nio.file.FileSystem"
- },
- "java.nio.file.spi.FileSystemProvider": {
- "j$.nio.file.spi.FileSystemProvider": "java.nio.file.spi.FileSystemProvider"
- },
- "java.nio.file.Path": {
- "j$.nio.file.Path": "java.nio.file.Path"
- },
"java.time.": {
"j$.time.": "java.time."
},
- "java.nio.file.WatchEvent": {
- "j$.nio.file.WatchEvent": "java.nio.file.WatchEvent"
+ "java.nio.file.": {
+ "j$.nio.file.": "java.nio.file."
}
},
"retarget_method": {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifier.java b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifier.java
index 44f3fde..58e9d2a 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifier.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifier.java
@@ -117,6 +117,11 @@
if (appView.options().enableCheckAllInstructionsDuringStackMapVerification
|| instruction.canThrow()) {
state = checkExceptionEdges(state, labelToFrameMap);
+ if (state.isError()) {
+ return fail(
+ CfCodeStackMapValidatingException.invalidStackMapForInstruction(
+ method, i, instruction, state.asError().getMessage(), appView));
+ }
}
}
eventConsumer.acceptInstructionState(instruction, state);
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index 96a4596..e23386b 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -1126,11 +1126,16 @@
boolean isValidOrder = stringIndex > prevStringIndex;
assert isValidOrder
: String.format(
- "Out-of-order type ids (type #%s: `%s`, type #%s: `%s`)",
+ (indexedItems.getString(prevStringIndex).equals(indexedItems.getString(stringIndex))
+ ? "Duplicate"
+ : "Out-of-order")
+ + " type ids (type #%s: `%s` string #%s, type #%s: `%s` string #%s)",
index - 1,
indexedItems.getString(prevStringIndex),
+ prevStringIndex,
index,
- indexedItems.getString(stringIndex));
+ indexedItems.getString(stringIndex),
+ stringIndex);
prevStringIndex = stringIndex;
}
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 5b71a42..169a880 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -898,7 +898,8 @@
}
@Override
- public Code getCodeAsInlining(DexMethod caller, DexMethod callee, DexItemFactory factory) {
+ public Code getCodeAsInlining(
+ DexMethod caller, DexMethod callee, DexItemFactory factory, boolean isCalleeD8R8Synthesized) {
Position callerPosition = SyntheticPosition.builder().setLine(0).setMethod(caller).build();
List<CfInstruction> newInstructions = new ArrayList<>(instructions.size() + 2);
CfLabel firstLabel;
@@ -916,15 +917,18 @@
newInstructions.add(
new CfPosition(
oldPosition.getLabel(),
- oldPosition.getPosition().withOutermostCallerPosition(callerPosition)));
+ newInlineePosition(
+ callerPosition, oldPosition.getPosition(), isCalleeD8R8Synthesized)));
} else {
if (!instruction.isLabel() && !seenPosition) {
Position preamblePosition =
- SyntheticPosition.builder()
- .setMethod(callee)
- .setCallerPosition(callerPosition)
- .setLine(0)
- .build();
+ isCalleeD8R8Synthesized
+ ? callerPosition
+ : SyntheticPosition.builder()
+ .setMethod(callee)
+ .setCallerPosition(callerPosition)
+ .setLine(0)
+ .build();
newInstructions.add(new CfPosition(firstLabel, preamblePosition));
seenPosition = true;
}
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 d0417b4..0475e2c 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.NumberGenerator;
import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.Position.PositionBuilder;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.RetracerForCodePrinting;
@@ -176,7 +177,35 @@
return true;
}
- public Code getCodeAsInlining(DexMethod caller, DexMethod callee, DexItemFactory factory) {
+ public Code getCodeAsInlining(DexMethod caller, DexEncodedMethod callee, DexItemFactory factory) {
+ return getCodeAsInlining(caller, callee.getReference(), factory, callee.isD8R8Synthesized());
+ }
+
+ public Code getCodeAsInlining(
+ DexMethod caller, DexMethod callee, DexItemFactory factory, boolean isCalleeD8R8Synthesized) {
throw new Unreachable();
}
+
+ public Position newInlineePosition(
+ Position callerPosition, Position oldPosition, boolean isCalleeD8R8Synthesized) {
+ Position outermostCaller = oldPosition.getOutermostCaller();
+ if (!isCalleeD8R8Synthesized) {
+ return oldPosition.withOutermostCallerPosition(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.
+ if (!outermostCaller.isOutline() && !outermostCaller.isRemoveInnerFramesIfThrowingNpe()) {
+ return oldPosition.replacePosition(outermostCaller, callerPosition);
+ }
+ assert !callerPosition.isOutline();
+ PositionBuilder<?, ?> positionBuilder =
+ outermostCaller
+ .builderWithCopy()
+ .setMethod(callerPosition.getMethod())
+ .setLine(callerPosition.getLine());
+ if (callerPosition.isRemoveInnerFramesIfThrowingNpe()) {
+ positionBuilder.setRemoveInnerFramesIfThrowingNpe(true);
+ }
+ return oldPosition.replacePosition(outermostCaller, positionBuilder.build());
+ }
}
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 048e01d..8ca811b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -279,7 +279,8 @@
}
@Override
- public Code getCodeAsInlining(DexMethod caller, DexMethod callee, DexItemFactory factory) {
+ public Code getCodeAsInlining(
+ DexMethod caller, DexMethod callee, DexItemFactory factory, boolean isCalleeD8R8Synthesized) {
return new DexCode(
registerSize,
incomingRegisterSize,
@@ -287,11 +288,11 @@
instructions,
tries,
handlers,
- debugInfoAsInlining(caller, callee, factory));
+ debugInfoAsInlining(caller, callee, isCalleeD8R8Synthesized, factory));
}
private DexDebugInfo debugInfoAsInlining(
- DexMethod caller, DexMethod callee, DexItemFactory factory) {
+ DexMethod caller, DexMethod callee, boolean isCalleeD8R8Synthesized, DexItemFactory factory) {
Position callerPosition = SyntheticPosition.builder().setLine(0).setMethod(caller).build();
EventBasedDebugInfo eventBasedInfo = DexDebugInfo.convertToEventBased(this, factory);
if (eventBasedInfo == null) {
@@ -299,11 +300,13 @@
// This is consistent with the building IR for inlining which will always ensure the method
// has a position.
Position preamblePosition =
- SyntheticPosition.builder()
- .setMethod(callee)
- .setCallerPosition(callerPosition)
- .setLine(0)
- .build();
+ isCalleeD8R8Synthesized
+ ? callerPosition
+ : SyntheticPosition.builder()
+ .setMethod(callee)
+ .setCallerPosition(callerPosition)
+ .setLine(0)
+ .build();
return new EventBasedDebugInfo(
0,
new DexString[callee.getArity()],
@@ -321,14 +324,17 @@
int i = 0;
newEvents[i++] =
new SetPositionFrame(
- frameBuilder.setMethod(callee).setCallerPosition(callerPosition).build());
+ isCalleeD8R8Synthesized
+ ? callerPosition
+ : frameBuilder.setMethod(callee).setCallerPosition(callerPosition).build());
for (DexDebugEvent event : oldEvents) {
if (event instanceof SetPositionFrame) {
SetPositionFrame oldFrame = (SetPositionFrame) event;
assert oldFrame.getPosition() != null;
newEvents[i++] =
new SetPositionFrame(
- oldFrame.getPosition().withOutermostCallerPosition(callerPosition));
+ newInlineePosition(
+ callerPosition, oldFrame.getPosition(), isCalleeD8R8Synthesized));
} else {
newEvents[i++] = event;
}
@@ -554,7 +560,7 @@
.append("(warning: has unhandled debug events @ pc:")
.append(debugInfo.address)
.append(", line:")
- .append(debugInfo.line);
+ .append(debugInfo.getPosition().getLine());
} else {
builder.append("(has debug events past last pc)\n");
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java b/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java
index f89353c..6679383 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java
@@ -4,60 +4,38 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.Position.OutlineCallerPosition;
-import com.android.tools.r8.ir.code.Position.OutlineCallerPosition.OutlineCallerPositionBuilder;
-import com.android.tools.r8.ir.code.Position.OutlinePosition;
-import com.android.tools.r8.ir.code.Position.PositionBuilder;
-import com.android.tools.r8.ir.code.Position.SourcePosition;
-import com.android.tools.r8.utils.Int2StructuralItemArrayMap;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
-import java.util.function.Function;
public class DexDebugEntry {
public final boolean lineEntry;
public final int address;
- public final int line;
public final DexString sourceFile;
public final boolean prologueEnd;
public final boolean epilogueBegin;
public final Map<Integer, DebugLocalInfo> locals;
- public final DexMethod method;
- public final Position callerPosition;
- public final boolean isOutline;
- public final DexMethod outlineCallee;
- public final Int2StructuralItemArrayMap<Position> outlineCallerPositions;
+ private final Position position;
public DexDebugEntry(
boolean lineEntry,
int address,
- int line,
DexString sourceFile,
boolean prologueEnd,
boolean epilogueBegin,
ImmutableMap<Integer, DebugLocalInfo> locals,
- DexMethod method,
- Position callerPosition,
- boolean isOutline,
- DexMethod outlineCallee,
- Int2StructuralItemArrayMap<Position> outlineCallerPositions) {
+ Position position) {
this.lineEntry = lineEntry;
this.address = address;
- this.line = line;
this.sourceFile = sourceFile;
this.prologueEnd = prologueEnd;
this.epilogueBegin = epilogueBegin;
this.locals = locals;
- this.method = method;
- assert method != null;
- this.callerPosition = callerPosition;
- this.isOutline = isOutline;
- this.outlineCallee = outlineCallee;
- this.outlineCallerPositions = outlineCallerPositions;
+ this.position = position;
+ assert position != null;
}
@Override
@@ -74,24 +52,7 @@
if (sourceFile != null) {
builder.append(", file ").append(sourceFile);
}
- builder.append(", line ").append(line);
- if (callerPosition != null) {
- builder.append(":").append(method.name);
- Position caller = callerPosition;
- while (caller != null) {
- builder.append(";").append(caller.getLine()).append(":").append(caller.getMethod().name);
- caller = caller.getCallerPosition();
- }
- }
- if (isOutline) {
- builder.append(", isOutline = true");
- }
- if (outlineCallee != null) {
- builder.append(", outlineCallee = ").append(outlineCallee);
- }
- if (outlineCallerPositions != null) {
- builder.append(", outlineCallerPositions = ").append(outlineCallerPositions);
- }
+ builder.append(", ").append(position);
if (prologueEnd) {
builder.append(", prologue_end = true");
}
@@ -115,22 +76,11 @@
return builder.toString();
}
- public Position toPosition(Function<Position, Position> canonicalizeCallerPosition) {
- PositionBuilder<?, ?> positionBuilder;
- if (outlineCallee != null) {
- OutlineCallerPositionBuilder outlineCallerPositionBuilder =
- OutlineCallerPosition.builder().setOutlineCallee(outlineCallee).setIsOutline(isOutline);
- outlineCallerPositions.forEach(outlineCallerPositionBuilder::addOutlinePosition);
- positionBuilder = outlineCallerPositionBuilder;
- } else if (isOutline) {
- positionBuilder = OutlinePosition.builder();
- } else {
- positionBuilder = SourcePosition.builder().setFile(sourceFile);
- }
- return positionBuilder
- .setLine(line)
- .setMethod(method)
- .setCallerPosition(canonicalizeCallerPosition.apply(callerPosition))
- .build();
+ public Position getPosition() {
+ return position;
+ }
+
+ public int getLine() {
+ return position.getLine();
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
index 5a4fd84..e323ab8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
@@ -46,7 +46,6 @@
private boolean epilogueBegin = false;
private final Map<Integer, LocalEntry> locals = new HashMap<>();
private final Int2ReferenceMap<DebugLocalInfo> arguments = new Int2ReferenceArrayMap<>();
- private final DexMethod method;
// Delayed construction of an entry. Is finalized once locals information has been collected.
private DexDebugEntry pending = null;
@@ -60,13 +59,11 @@
public DexDebugEntryBuilder(int startLine, DexMethod method) {
assert method != null;
- this.method = method;
positionState = new DexDebugPositionState(startLine, method);
}
public DexDebugEntryBuilder(DexEncodedMethod method, DexItemFactory factory) {
assert method != null && method.getReference() != null;
- this.method = method.getReference();
DexCode code = method.getCode().asDexCode();
EventBasedDebugInfo info = code.getDebugInfo().asEventBasedInfo();
// Only event based debug info supports conversion to entries.
@@ -164,31 +161,21 @@
new DexDebugEntry(
pending.lineEntry,
pending.address,
- pending.line,
pending.sourceFile,
pending.prologueEnd,
pending.epilogueBegin,
getLocals(),
- pending.method,
- pending.callerPosition,
- pending.isOutline,
- pending.outlineCallee,
- pending.outlineCallerPositions));
+ pending.getPosition()));
}
pending =
new DexDebugEntry(
lineEntry,
positionState.getCurrentPc(),
- positionState.getCurrentLine(),
- positionState.getCurrentFile(),
+ null,
prologueEnd,
epilogueBegin,
null,
- positionState.getCurrentMethod(),
- positionState.getCurrentCallerPosition(),
- positionState.isOutline(),
- positionState.getOutlineCallee(),
- positionState.getOutlineCallerPositions());
+ positionState.getPosition());
prologueEnd = false;
epilogueBegin = false;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugPositionState.java b/src/main/java/com/android/tools/r8/graph/DexDebugPositionState.java
index 10a06d5..96eb206 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugPositionState.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugPositionState.java
@@ -15,7 +15,8 @@
import com.android.tools.r8.graph.DexDebugEvent.SetPrologueEnd;
import com.android.tools.r8.graph.DexDebugEvent.StartLocal;
import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.utils.Int2StructuralItemArrayMap;
+import com.android.tools.r8.ir.code.Position.SourcePosition;
+import com.android.tools.r8.ir.code.Position.SyntheticPosition;
/**
* State machine to process and accumulate position-related DexDebugEvents. Clients should retrieve
@@ -25,12 +26,8 @@
private int currentPc = 0;
private int currentLine;
- private DexString currentFile = null;
- private DexMethod currentMethod;
- private Position currentCallerPosition = null;
- private boolean isOutline;
- private DexMethod outlineCallee;
- private Int2StructuralItemArrayMap<Position> outlineCallerPositions;
+ protected DexMethod currentMethod;
+ protected Position currentPosition;
public DexDebugPositionState(int startLine, DexMethod method) {
currentLine = startLine;
@@ -53,10 +50,7 @@
assert setPositionFrame.getPosition() != null;
Position position = setPositionFrame.getPosition();
currentMethod = position.getMethod();
- currentCallerPosition = position.getCallerPosition();
- outlineCallee = position.getOutlineCallee();
- outlineCallerPositions = position.getOutlinePositions();
- isOutline = position.isOutline();
+ currentPosition = position;
}
@Override
@@ -68,7 +62,7 @@
@Override
public void visit(SetFile setFile) {
- currentFile = setFile.fileName;
+ // Empty.
}
@Override
@@ -104,33 +98,14 @@
return currentLine;
}
- public DexString getCurrentFile() {
- return currentFile;
- }
-
- public DexMethod getCurrentMethod() {
- return currentMethod;
- }
-
- public Position getCurrentCallerPosition() {
- return currentCallerPosition;
- }
-
- public boolean isOutline() {
- return isOutline;
- }
-
- public DexMethod getOutlineCallee() {
- return outlineCallee;
- }
-
- public Int2StructuralItemArrayMap<Position> getOutlineCallerPositions() {
- return outlineCallerPositions;
- }
-
- public void resetOutlineInformation() {
- isOutline = false;
- outlineCallee = null;
- outlineCallerPositions = null;
+ public Position getPosition() {
+ if (currentPosition == null) {
+ return (getCurrentLine() > 0 ? SourcePosition.builder() : SyntheticPosition.builder())
+ .setLine(getCurrentLine())
+ .setMethod(currentMethod)
+ .build();
+ } else {
+ return currentPosition.builderWithCopy().setLine(getCurrentLine()).build();
+ }
}
}
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 888bbfe..94b8840 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -2289,7 +2289,7 @@
private final Set<DexString> varHandleSetMethods =
createStrings(setString, "setOpaque", "setRelease", "setVolatile");
- private final Set<DexString> varHandleCompareAndSetMethods =
+ public final Set<DexString> varHandleCompareAndSetMethodNames =
createStrings(
compareAndSetString,
"weakCompareAndSet",
@@ -2308,7 +2308,7 @@
result = createMethod(varHandleType, signature, invokeProto.name);
} else if (varHandleSetMethods.contains(invokeProto.name)) {
result = createMethod(varHandleType, setSignature, invokeProto.name);
- } else if (varHandleCompareAndSetMethods.contains(invokeProto.name)) {
+ } else if (varHandleCompareAndSetMethodNames.contains(invokeProto.name)) {
result = createMethod(varHandleType, compareAndSetSignature, invokeProto.name);
}
}
@@ -2333,7 +2333,7 @@
if (invokeProto.holder == varHandleType) {
return varHandleMethods.contains(invokeProto.name)
|| varHandleSetMethods.contains(invokeProto.name)
- || varHandleCompareAndSetMethods.contains(invokeProto.name);
+ || varHandleCompareAndSetMethodNames.contains(invokeProto.name);
}
return false;
}
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index 3abc2e4..d98d0ea 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -175,7 +175,7 @@
}
@Override
- public Code getCodeAsInlining(DexMethod caller, DexMethod callee, DexItemFactory factory) {
+ public Code getCodeAsInlining(DexMethod caller, DexEncodedMethod callee, DexItemFactory factory) {
return asCfCode().getCodeAsInlining(caller, callee, factory);
}
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 653e20e..5e88574 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
@@ -46,10 +46,6 @@
return false;
}
- public boolean isAdditionalMappingInfoPosition() {
- return false;
- }
-
public boolean isRemoveInnerFramesIfThrowingNpe() {
return removeInnerFramesIfThrowingNpe;
}
@@ -201,7 +197,8 @@
return HashCodeVisitor.run(this);
}
- private String toString(boolean forceMethod) {
+ @Override
+ public String toString() {
if (isNone()) {
return "--";
}
@@ -210,7 +207,7 @@
builder.append(getFile()).append(":");
}
builder.append("#").append(line);
- if (method != null && (forceMethod || callerPosition != null)) {
+ if (method != null && callerPosition != null) {
builder.append(":").append(method.name);
}
if (callerPosition != null) {
@@ -220,14 +217,18 @@
caller = caller.callerPosition;
}
}
+ if (isOutline()) {
+ builder.append(", isOutline = true");
+ }
+ if (getOutlineCallee() != null) {
+ builder.append(", outlineCallee = ").append(getOutlineCallee());
+ }
+ if (getOutlinePositions() != null) {
+ builder.append(", outlineCallerPositions = ").append(getOutlinePositions());
+ }
return builder.toString();
}
- @Override
- public String toString() {
- return toString(false);
- }
-
public abstract PositionBuilder<?, ?> builderWithCopy();
public abstract static class PositionBuilder<
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 3fbcdc8..ea0f655 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
@@ -259,10 +259,15 @@
private Position getCanonicalPositionAppendCaller(DexDebugEntry entry) {
// If this instruction has already been inlined then this.method must be the outermost caller.
- assert entry.callerPosition == null
- || entry.callerPosition.getOutermostCaller().getMethod() == originalMethod;
+ Position position = entry.getPosition();
+ assert !position.hasCallerPosition()
+ || position.getOutermostCaller().getMethod() == originalMethod;
return canonicalPositions.getCanonical(
- entry.toPosition(canonicalPositions::canonicalizeCallerPosition));
+ position
+ .builderWithCopy()
+ .setCallerPosition(
+ canonicalPositions.canonicalizeCallerPosition(position.getCallerPosition()))
+ .build());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
index 8d2c8c6..adf23fa 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -210,7 +210,7 @@
definition
.getCode()
.getCodeAsInlining(
- companion.getReference(), method.getReference(), appView.dexItemFactory());
+ companion.getReference(), method.getDefinition(), appView.dexItemFactory());
if (!definition.isStatic()) {
DexEncodedMethod.setDebugInfoWithFakeThisParameter(
code, companion.getReference().getArity(), appView);
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 9ed7493..ae232bd 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
@@ -379,7 +379,7 @@
}
private DexType objectOrPrimitiveReturnType(DexType type) {
- return type.isPrimitiveType() || type.isVoidType() ? type : factory.objectType;
+ return isPrimitiveThatIsNotBoxed(type) || type.isVoidType() ? type : factory.objectType;
}
private DexType objectOrPrimitiveParameterType(DexType type) {
@@ -456,13 +456,23 @@
}
}
assert newParameters.size() == proto.parameters.size();
- // TODO(b/247076137): Also convert return type if reference type and not Object?.
+ DexString name = invoke.getMethod().getName();
DexProto newProto =
- factory.createProto(objectOrPrimitiveReturnType(proto.returnType), newParameters);
- DexMethod newMethod =
- factory.createMethod(factory.desugarVarHandleType, newProto, invoke.getMethod().getName());
+ factory.createProto(
+ factory.polymorphicMethods.varHandleCompareAndSetMethodNames.contains(name)
+ ? proto.returnType
+ : objectOrPrimitiveReturnType(proto.returnType),
+ newParameters);
+ DexMethod newMethod = factory.createMethod(factory.desugarVarHandleType, newProto, name);
builder.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, newMethod, false));
- if (proto.returnType.isClassType()
+ if (proto.returnType.isPrimitiveType() && !newProto.returnType.isPrimitiveType()) {
+ assert proto.returnType.isPrimitiveType();
+ assert newProto.returnType == factory.objectType;
+ builder.add(new CfCheckCast(factory.getBoxedForPrimitiveType(proto.returnType)));
+ builder.add(
+ new CfInvoke(
+ Opcodes.INVOKEVIRTUAL, factory.getUnboxPrimitiveMethod(proto.returnType), false));
+ } else if (proto.returnType.isClassType()
&& proto.returnType != factory.objectType
&& proto.returnType != factory.voidType) {
builder.add(new CfCheckCast(proto.returnType));
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 e32fa57..112f246 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
@@ -52,6 +52,7 @@
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;");
@@ -98,6 +99,11 @@
.setAccessFlags(FieldAccessFlags.createPublicFinalSynthetic())
.disableAndroidApiLevelCheck()
.build()));
+ DexMethod toIntIfPossible =
+ factory.createMethod(
+ builder.getType(),
+ factory.createProto(factory.intType, factory.objectType, factory.booleanType),
+ factory.createString("toIntIfPossible"));
DexMethod get =
factory.createMethod(
builder.getType(),
@@ -120,11 +126,6 @@
builder.getType(),
factory.createProto(factory.voidType, factory.objectType, factory.intType),
factory.createString("set"));
- DexMethod toLongIfPossible =
- factory.createMethod(
- builder.getType(),
- factory.createProto(factory.longType, factory.objectType),
- factory.createString("toLongIfPossible"));
DexMethod constructor_3 =
factory.createMethod(
builder.getType(),
@@ -145,6 +146,11 @@
factory.createProto(
factory.booleanType, factory.objectType, factory.longType, factory.longType),
factory.createString("compareAndSet"));
+ DexMethod toLongIfPossible =
+ factory.createMethod(
+ builder.getType(),
+ factory.createProto(factory.longType, factory.objectType, factory.booleanType),
+ factory.createString("toLongIfPossible"));
DexMethod set =
factory.createMethod(
builder.getType(),
@@ -167,11 +173,6 @@
builder.getType(),
factory.createProto(factory.intType, factory.objectType),
factory.createString("get"));
- DexMethod toIntIfPossible =
- factory.createMethod(
- builder.getType(),
- factory.createProto(factory.intType, factory.objectType),
- factory.createString("toIntIfPossible"));
DexMethod getLong =
factory.createMethod(
builder.getType(),
@@ -198,6 +199,14 @@
builder.setVirtualMethods(
ImmutableList.of(
DexEncodedMethod.syntheticBuilder()
+ .setMethod(toIntIfPossible)
+ .setAccessFlags(
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC, false))
+ .setCode(DesugarVarHandle_toIntIfPossible(factory, toIntIfPossible))
+ .disableAndroidApiLevelCheck()
+ .build(),
+ DexEncodedMethod.syntheticBuilder()
.setMethod(get)
.setAccessFlags(
MethodAccessFlags.fromSharedAccessFlags(
@@ -222,14 +231,6 @@
.disableAndroidApiLevelCheck()
.build(),
DexEncodedMethod.syntheticBuilder()
- .setMethod(toLongIfPossible)
- .setAccessFlags(
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC, false))
- .setCode(DesugarVarHandle_toLongIfPossible(factory, toLongIfPossible))
- .disableAndroidApiLevelCheck()
- .build(),
- DexEncodedMethod.syntheticBuilder()
.setMethod(setLong)
.setAccessFlags(
MethodAccessFlags.fromSharedAccessFlags(
@@ -246,6 +247,14 @@
.disableAndroidApiLevelCheck()
.build(),
DexEncodedMethod.syntheticBuilder()
+ .setMethod(toLongIfPossible)
+ .setAccessFlags(
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC, false))
+ .setCode(DesugarVarHandle_toLongIfPossible(factory, toLongIfPossible))
+ .disableAndroidApiLevelCheck()
+ .build(),
+ DexEncodedMethod.syntheticBuilder()
.setMethod(set)
.setAccessFlags(
MethodAccessFlags.fromSharedAccessFlags(
@@ -280,14 +289,6 @@
.disableAndroidApiLevelCheck()
.build(),
DexEncodedMethod.syntheticBuilder()
- .setMethod(toIntIfPossible)
- .setAccessFlags(
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC, false))
- .setCode(DesugarVarHandle_toIntIfPossible(factory, toIntIfPossible))
- .disableAndroidApiLevelCheck()
- .build(),
- DexEncodedMethod.syntheticBuilder()
.setMethod(getLong)
.setAccessFlags(
MethodAccessFlags.fromSharedAccessFlags(
@@ -659,7 +660,7 @@
CfLabel label9 = new CfLabel();
return new CfCode(
method.holder,
- 8,
+ 9,
4,
ImmutableList.of(
label0,
@@ -691,21 +692,23 @@
factory.createString("offset"))),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.OBJECT, 2),
+ new CfConstNumber(0, ValueType.INT),
label2,
new CfInvoke(
182,
factory.createMethod(
factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
- factory.createProto(factory.intType, factory.objectType),
+ factory.createProto(factory.intType, factory.objectType, factory.booleanType),
factory.createString("toIntIfPossible")),
false),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.OBJECT, 3),
+ new CfConstNumber(0, ValueType.INT),
new CfInvoke(
182,
factory.createMethod(
factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
- factory.createProto(factory.intType, factory.objectType),
+ factory.createProto(factory.intType, factory.objectType, factory.booleanType),
factory.createString("toIntIfPossible")),
false),
label3,
@@ -761,21 +764,23 @@
factory.createString("offset"))),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.OBJECT, 2),
+ new CfConstNumber(0, ValueType.INT),
label6,
new CfInvoke(
182,
factory.createMethod(
factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
- factory.createProto(factory.longType, factory.objectType),
+ factory.createProto(factory.longType, factory.objectType, factory.booleanType),
factory.createString("toLongIfPossible")),
false),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.OBJECT, 3),
+ new CfConstNumber(0, ValueType.INT),
new CfInvoke(
182,
factory.createMethod(
factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
- factory.createProto(factory.longType, factory.objectType),
+ factory.createProto(factory.longType, factory.objectType, factory.booleanType),
factory.createString("toLongIfPossible")),
false),
label7,
@@ -1259,9 +1264,13 @@
public static CfCode DesugarVarHandle_getInt(DexItemFactory factory, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
+ CfLabel label2 = new CfLabel();
+ CfLabel label3 = new CfLabel();
+ CfLabel label4 = new CfLabel();
+ CfLabel label5 = new CfLabel();
return new CfCode(
method.holder,
- 4,
+ 5,
2,
ImmutableList.of(
label0,
@@ -1269,6 +1278,19 @@
new CfInstanceFieldRead(
factory.createField(
factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.classType,
+ factory.createString("type"))),
+ new CfStaticFieldRead(
+ factory.createField(
+ factory.createType("Ljava/lang/Integer;"),
+ factory.classType,
+ factory.createString("TYPE"))),
+ new CfIfCmp(If.Type.NE, ValueType.OBJECT, label2),
+ label1,
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInstanceFieldRead(
+ factory.createField(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 1),
@@ -1286,7 +1308,90 @@
factory.createString("getInt")),
false),
new CfReturn(ValueType.INT),
- label1),
+ label2,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ FrameType.initializedNonNullReference(factory.objectType)
+ })),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInstanceFieldRead(
+ factory.createField(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.classType,
+ factory.createString("type"))),
+ new CfStaticFieldRead(
+ factory.createField(
+ factory.createType("Ljava/lang/Long;"),
+ factory.classType,
+ factory.createString("TYPE"))),
+ new CfIfCmp(If.Type.NE, ValueType.OBJECT, label4),
+ label3,
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInstanceFieldRead(
+ factory.createField(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ 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.longType,
+ factory.createString("offset"))),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lsun/misc/Unsafe;"),
+ factory.createProto(factory.longType, factory.objectType, factory.longType),
+ factory.createString("getLong")),
+ false),
+ new CfNumberConversion(NumericType.LONG, NumericType.INT),
+ new CfReturn(ValueType.INT),
+ label4,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ 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("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.longType,
+ factory.createString("offset"))),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lsun/misc/Unsafe;"),
+ factory.createProto(factory.objectType, factory.objectType, factory.longType),
+ factory.createString("getObject")),
+ false),
+ new CfConstNumber(1, ValueType.INT),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createProto(factory.intType, factory.objectType, factory.booleanType),
+ factory.createString("toIntIfPossible")),
+ false),
+ new CfReturn(ValueType.INT),
+ label5),
ImmutableList.of(),
ImmutableList.of());
}
@@ -1294,9 +1399,13 @@
public static CfCode DesugarVarHandle_getLong(DexItemFactory factory, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
+ CfLabel label2 = new CfLabel();
+ CfLabel label3 = new CfLabel();
+ CfLabel label4 = new CfLabel();
+ CfLabel label5 = new CfLabel();
return new CfCode(
method.holder,
- 4,
+ 5,
2,
ImmutableList.of(
label0,
@@ -1304,6 +1413,19 @@
new CfInstanceFieldRead(
factory.createField(
factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.classType,
+ factory.createString("type"))),
+ new CfStaticFieldRead(
+ factory.createField(
+ factory.createType("Ljava/lang/Long;"),
+ factory.classType,
+ factory.createString("TYPE"))),
+ new CfIfCmp(If.Type.NE, ValueType.OBJECT, label2),
+ label1,
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInstanceFieldRead(
+ factory.createField(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
factory.createType("Lsun/misc/Unsafe;"),
factory.createString("U"))),
new CfLoad(ValueType.OBJECT, 1),
@@ -1321,7 +1443,90 @@
factory.createString("getLong")),
false),
new CfReturn(ValueType.LONG),
- label1),
+ label2,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ FrameType.initializedNonNullReference(factory.objectType)
+ })),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInstanceFieldRead(
+ factory.createField(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.classType,
+ factory.createString("type"))),
+ new CfStaticFieldRead(
+ factory.createField(
+ factory.createType("Ljava/lang/Integer;"),
+ factory.classType,
+ factory.createString("TYPE"))),
+ new CfIfCmp(If.Type.NE, ValueType.OBJECT, label4),
+ label3,
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInstanceFieldRead(
+ factory.createField(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ 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.longType,
+ factory.createString("offset"))),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lsun/misc/Unsafe;"),
+ factory.createProto(factory.intType, factory.objectType, factory.longType),
+ factory.createString("getInt")),
+ false),
+ new CfNumberConversion(NumericType.INT, NumericType.LONG),
+ new CfReturn(ValueType.LONG),
+ label4,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ 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("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.longType,
+ factory.createString("offset"))),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lsun/misc/Unsafe;"),
+ factory.createProto(factory.objectType, factory.objectType, factory.longType),
+ factory.createString("getObject")),
+ false),
+ new CfConstNumber(1, ValueType.INT),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createProto(factory.longType, factory.objectType, factory.booleanType),
+ factory.createString("toLongIfPossible")),
+ false),
+ new CfReturn(ValueType.LONG),
+ label5),
ImmutableList.of(),
ImmutableList.of());
}
@@ -1357,11 +1562,12 @@
new CfLoad(ValueType.OBJECT, 1),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.OBJECT, 2),
+ new CfConstNumber(0, ValueType.INT),
new CfInvoke(
182,
factory.createMethod(
factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
- factory.createProto(factory.intType, factory.objectType),
+ factory.createProto(factory.intType, factory.objectType, factory.booleanType),
factory.createString("toIntIfPossible")),
false),
new CfInvoke(
@@ -1399,11 +1605,12 @@
new CfLoad(ValueType.OBJECT, 1),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.OBJECT, 2),
+ new CfConstNumber(0, ValueType.INT),
new CfInvoke(
182,
factory.createMethod(
factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
- factory.createProto(factory.longType, factory.objectType),
+ factory.createProto(factory.longType, factory.objectType, factory.booleanType),
factory.createString("toLongIfPossible")),
false),
new CfInvoke(
@@ -1608,6 +1815,8 @@
CfLabel label2 = new CfLabel();
CfLabel label3 = new CfLabel();
CfLabel label4 = new CfLabel();
+ CfLabel label5 = new CfLabel();
+ CfLabel label6 = new CfLabel();
return new CfCode(
method.holder,
6,
@@ -1649,7 +1858,7 @@
factory.voidType, factory.objectType, factory.longType, factory.longType),
factory.createString("putLong")),
false),
- new CfGoto(label3),
+ new CfGoto(label5),
label2,
new CfFrame(
new Int2ObjectAVLTreeMap<>(
@@ -1662,6 +1871,19 @@
FrameType.longHighType()
})),
new CfLoad(ValueType.OBJECT, 0),
+ new CfInstanceFieldRead(
+ factory.createField(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.classType,
+ factory.createString("type"))),
+ new CfStaticFieldRead(
+ factory.createField(
+ factory.createType("Ljava/lang/Integer;"),
+ factory.classType,
+ factory.createString("TYPE"))),
+ new CfIfCmp(If.Type.NE, ValueType.OBJECT, label4),
+ label3,
+ new CfLoad(ValueType.OBJECT, 0),
new CfInvoke(
182,
factory.createMethod(
@@ -1670,7 +1892,47 @@
factory.createString("desugarWrongMethodTypeException")),
false),
new CfThrow(),
- label3,
+ label4,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1, 2, 3},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ FrameType.initializedNonNullReference(factory.objectType),
+ FrameType.longType(),
+ FrameType.longHighType()
+ })),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInstanceFieldRead(
+ factory.createField(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ 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.longType,
+ factory.createString("offset"))),
+ new CfLoad(ValueType.LONG, 2),
+ new CfInvoke(
+ 184,
+ factory.createMethod(
+ factory.createType("Ljava/lang/Long;"),
+ factory.createProto(factory.createType("Ljava/lang/Long;"), factory.longType),
+ factory.createString("valueOf")),
+ false),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lsun/misc/Unsafe;"),
+ factory.createProto(
+ factory.voidType, factory.objectType, factory.longType, factory.objectType),
+ factory.createString("putObject")),
+ false),
+ label5,
new CfFrame(
new Int2ObjectAVLTreeMap<>(
new int[] {0, 1, 2, 3},
@@ -1682,7 +1944,7 @@
FrameType.longHighType()
})),
new CfReturnVoid(),
- label4),
+ label6),
ImmutableList.of(),
ImmutableList.of());
}
@@ -1698,10 +1960,12 @@
CfLabel label7 = new CfLabel();
CfLabel label8 = new CfLabel();
CfLabel label9 = new CfLabel();
+ CfLabel label10 = new CfLabel();
+ CfLabel label11 = new CfLabel();
return new CfCode(
method.holder,
- 1,
2,
+ 3,
ImmutableList.of(
label0,
new CfLoad(ValueType.OBJECT, 1),
@@ -1721,11 +1985,12 @@
label2,
new CfFrame(
new Int2ObjectAVLTreeMap<>(
- new int[] {0, 1},
+ new int[] {0, 1, 2},
new FrameType[] {
FrameType.initializedNonNullReference(
factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
- FrameType.initializedNonNullReference(factory.objectType)
+ FrameType.initializedNonNullReference(factory.objectType),
+ FrameType.intType()
})),
new CfLoad(ValueType.OBJECT, 1),
new CfInstanceOf(factory.createType("Ljava/lang/Byte;")),
@@ -1744,11 +2009,12 @@
label4,
new CfFrame(
new Int2ObjectAVLTreeMap<>(
- new int[] {0, 1},
+ new int[] {0, 1, 2},
new FrameType[] {
FrameType.initializedNonNullReference(
factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
- FrameType.initializedNonNullReference(factory.objectType)
+ FrameType.initializedNonNullReference(factory.objectType),
+ FrameType.intType()
})),
new CfLoad(ValueType.OBJECT, 1),
new CfInstanceOf(factory.boxedCharType),
@@ -1767,11 +2033,12 @@
label6,
new CfFrame(
new Int2ObjectAVLTreeMap<>(
- new int[] {0, 1},
+ new int[] {0, 1, 2},
new FrameType[] {
FrameType.initializedNonNullReference(
factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
- FrameType.initializedNonNullReference(factory.objectType)
+ FrameType.initializedNonNullReference(factory.objectType),
+ FrameType.intType()
})),
new CfLoad(ValueType.OBJECT, 1),
new CfInstanceOf(factory.createType("Ljava/lang/Short;")),
@@ -1790,11 +2057,35 @@
label8,
new CfFrame(
new Int2ObjectAVLTreeMap<>(
- new int[] {0, 1},
+ new int[] {0, 1, 2},
new FrameType[] {
FrameType.initializedNonNullReference(
factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
- FrameType.initializedNonNullReference(factory.objectType)
+ FrameType.initializedNonNullReference(factory.objectType),
+ FrameType.intType()
+ })),
+ new CfLoad(ValueType.INT, 2),
+ new CfIf(If.Type.EQ, ValueType.INT, label10),
+ label9,
+ new CfNew(factory.createType("Ljava/lang/ClassCastException;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfInvoke(
+ 183,
+ factory.createMethod(
+ factory.createType("Ljava/lang/ClassCastException;"),
+ factory.createProto(factory.voidType),
+ factory.createString("<init>")),
+ false),
+ new CfThrow(),
+ label10,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1, 2},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ FrameType.initializedNonNullReference(factory.objectType),
+ FrameType.intType()
})),
new CfLoad(ValueType.OBJECT, 0),
new CfInvoke(
@@ -1805,7 +2096,7 @@
factory.createString("desugarWrongMethodTypeException")),
false),
new CfThrow(),
- label9),
+ label11),
ImmutableList.of(),
ImmutableList.of());
}
@@ -1817,8 +2108,8 @@
CfLabel label3 = new CfLabel();
return new CfCode(
method.holder,
- 2,
- 2,
+ 3,
+ 3,
ImmutableList.of(
label0,
new CfLoad(ValueType.OBJECT, 1),
@@ -1838,19 +2129,21 @@
label2,
new CfFrame(
new Int2ObjectAVLTreeMap<>(
- new int[] {0, 1},
+ new int[] {0, 1, 2},
new FrameType[] {
FrameType.initializedNonNullReference(
factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
- FrameType.initializedNonNullReference(factory.objectType)
+ FrameType.initializedNonNullReference(factory.objectType),
+ FrameType.intType()
})),
new CfLoad(ValueType.OBJECT, 0),
new CfLoad(ValueType.OBJECT, 1),
+ new CfLoad(ValueType.INT, 2),
new CfInvoke(
182,
factory.createMethod(
factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
- factory.createProto(factory.intType, factory.objectType),
+ factory.createProto(factory.intType, factory.objectType, factory.booleanType),
factory.createString("toIntIfPossible")),
false),
new CfNumberConversion(NumericType.INT, NumericType.LONG),
diff --git a/src/main/java/com/android/tools/r8/utils/IntBox.java b/src/main/java/com/android/tools/r8/utils/IntBox.java
index 79684cd..47f2093 100644
--- a/src/main/java/com/android/tools/r8/utils/IntBox.java
+++ b/src/main/java/com/android/tools/r8/utils/IntBox.java
@@ -48,6 +48,12 @@
increment(1);
}
+ public void incrementIf(boolean value) {
+ if (value) {
+ increment();
+ }
+ }
+
public void increment(int i) {
assert i >= 0;
value += i;
diff --git a/src/main/java/com/android/tools/r8/utils/positions/ClassFilePositionToMappedRangeMapper.java b/src/main/java/com/android/tools/r8/utils/positions/ClassFilePositionToMappedRangeMapper.java
index dd60a3e..7360a6c 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/ClassFilePositionToMappedRangeMapper.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/ClassFilePositionToMappedRangeMapper.java
@@ -116,27 +116,7 @@
positionRemapper.createRemappedPosition(currentPosition);
Position oldPosition = remappedPosition.getFirst();
Position newPosition = remappedPosition.getSecond();
- if (isFirstEntry) {
- mappedPositions.add(
- new MappedPosition(
- oldPosition.getMethod(),
- oldPosition.getLine(),
- oldPosition.getCallerPosition(),
- newPosition.getLine(),
- oldPosition.isOutline(),
- oldPosition.getOutlineCallee(),
- oldPosition.getOutlinePositions()));
- } else {
- mappedPositions.add(
- new MappedPosition(
- oldPosition.getMethod(),
- oldPosition.getLine(),
- oldPosition.getCallerPosition(),
- newPosition.getLine(),
- false,
- null,
- null));
- }
+ mappedPositions.add(new MappedPosition(oldPosition, newPosition.getLine()));
CfPosition position = new CfPosition(new CfLabel(), newPosition);
newInstructions.add(position);
newInstructions.add(position.getLabel());
diff --git a/src/main/java/com/android/tools/r8/utils/positions/DexPositionToNoPcMappedRangeMapper.java b/src/main/java/com/android/tools/r8/utils/positions/DexPositionToNoPcMappedRangeMapper.java
index 6e540b8..a660a37 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/DexPositionToNoPcMappedRangeMapper.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/DexPositionToNoPcMappedRangeMapper.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.graph.DexDebugEvent.RestartLocal;
import com.android.tools.r8.graph.DexDebugEvent.SetEpilogueBegin;
import com.android.tools.r8.graph.DexDebugEvent.SetFile;
-import com.android.tools.r8.graph.DexDebugEvent.SetPositionFrame;
import com.android.tools.r8.graph.DexDebugEvent.SetPrologueEnd;
import com.android.tools.r8.graph.DexDebugEvent.StartLocal;
import com.android.tools.r8.graph.DexDebugEventBuilder;
@@ -26,12 +25,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.Position.OutlineCallerPosition;
-import com.android.tools.r8.ir.code.Position.OutlineCallerPosition.OutlineCallerPositionBuilder;
-import com.android.tools.r8.ir.code.Position.OutlinePosition;
-import com.android.tools.r8.ir.code.Position.PositionBuilder;
import com.android.tools.r8.ir.code.Position.SourcePosition;
-import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import java.util.ArrayList;
import java.util.List;
@@ -87,6 +81,122 @@
}
}
+ private static class DexDebugPositionStateVisitor extends DexDebugPositionState {
+
+ private final PositionEventEmitter positionEventEmitter;
+ private final List<MappedPosition> mappedPositions;
+ private final PositionRemapper positionRemapper;
+ private final List<DexDebugEvent> processedEvents;
+ private final DexItemFactory factory;
+
+ private final DexMethod startMethod;
+
+ // Keep track of what PC has been emitted.
+ private int emittedPc = 0;
+
+ private boolean inlinedOriginalPosition;
+
+ public DexDebugPositionStateVisitor(
+ PositionEventEmitter positionEventEmitter,
+ List<MappedPosition> mappedPositions,
+ PositionRemapper positionRemapper,
+ List<DexDebugEvent> processedEvents,
+ DexItemFactory factory,
+ int startLine,
+ DexMethod method) {
+ super(startLine, method);
+ this.positionEventEmitter = positionEventEmitter;
+ this.mappedPositions = mappedPositions;
+ this.positionRemapper = positionRemapper;
+ this.processedEvents = processedEvents;
+ this.factory = factory;
+ this.startMethod = method;
+ }
+
+ // Force the current PC to emitted.
+ private void flushPc() {
+ if (emittedPc != getCurrentPc()) {
+ positionEventEmitter.emitAdvancePc(getCurrentPc());
+ emittedPc = getCurrentPc();
+ }
+ }
+
+ // A default event denotes a line table entry and must always be emitted. Remap its line.
+ @Override
+ public void visit(Default defaultEvent) {
+ if (hasPreamblePosition(defaultEvent)) {
+ emitPreamblePosition();
+ }
+ super.visit(defaultEvent);
+ assert getCurrentLine() >= 0;
+ Position position = getPosition();
+ Position mappedPosition =
+ PositionUtils.remapAndAdd(position, positionRemapper, mappedPositions);
+ positionEventEmitter.emitPositionEvents(getCurrentPc(), mappedPosition);
+ if (mappedPosition != position) {
+ inlinedOriginalPosition = true;
+ }
+ emittedPc = getCurrentPc();
+ }
+
+ private boolean hasPreamblePosition(Default defaultEvent) {
+ return getCurrentPc() == 0
+ && defaultEvent.getPCDelta() > 0
+ && currentPosition != null
+ && currentPosition.getLine() != getCurrentLine();
+ }
+
+ // Non-materializing events use super, ie, AdvancePC, AdvanceLine and SetInlineFrame.
+
+ // Materializing events are just amended to the stream.
+
+ @Override
+ public void visit(SetFile setFile) {
+ processedEvents.add(setFile);
+ }
+
+ @Override
+ public void visit(SetPrologueEnd setPrologueEnd) {
+ processedEvents.add(setPrologueEnd);
+ }
+
+ @Override
+ public void visit(SetEpilogueBegin setEpilogueBegin) {
+ processedEvents.add(setEpilogueBegin);
+ }
+
+ // Local changes must force flush the PC ensuing they pertain to the correct point.
+
+ @Override
+ public void visit(StartLocal startLocal) {
+ flushPc();
+ processedEvents.add(startLocal);
+ }
+
+ @Override
+ public void visit(EndLocal endLocal) {
+ flushPc();
+ processedEvents.add(endLocal);
+ }
+
+ @Override
+ public void visit(RestartLocal restartLocal) {
+ flushPc();
+ processedEvents.add(restartLocal);
+ }
+
+ public void emitPreamblePosition() {
+ if (currentPosition == null || positionEventEmitter.didEmitLineEvents()) {
+ return;
+ }
+ Position mappedPosition =
+ PositionUtils.remapAndAdd(currentPosition, positionRemapper, mappedPositions);
+ processedEvents.add(factory.createPositionFrame(mappedPosition));
+ currentPosition = null;
+ currentMethod = startMethod;
+ }
+ }
+
private final AppView<?> appView;
private final boolean isIdentityMapping;
@@ -112,95 +222,24 @@
appView.graphLens().getOriginalMethodSignature(method.getReference()),
processedEvents);
- Box<Boolean> inlinedOriginalPosition = new Box<>(false);
-
- // Debug event visitor to map line numbers.
- DexDebugPositionState visitor =
- new DexDebugPositionState(
+ DexDebugPositionStateVisitor visitor =
+ new DexDebugPositionStateVisitor(
+ positionEventEmitter,
+ mappedPositions,
+ positionRemapper,
+ processedEvents,
+ appView.dexItemFactory(),
debugInfo.startLine,
- appView.graphLens().getOriginalMethodSignature(method.getReference())) {
-
- // Keep track of what PC has been emitted.
- private int emittedPc = 0;
-
- // Force the current PC to emitted.
- private void flushPc() {
- if (emittedPc != getCurrentPc()) {
- positionEventEmitter.emitAdvancePc(getCurrentPc());
- emittedPc = getCurrentPc();
- }
- }
-
- // A default event denotes a line table entry and must always be emitted. Remap its line.
- @Override
- public void visit(Default defaultEvent) {
- super.visit(defaultEvent);
- assert getCurrentLine() >= 0;
- Position position = getPositionFromPositionState(this);
- Position currentPosition =
- PositionUtils.remapAndAdd(position, positionRemapper, mappedPositions);
- positionEventEmitter.emitPositionEvents(getCurrentPc(), currentPosition);
- if (currentPosition != position) {
- inlinedOriginalPosition.set(true);
- }
- emittedPc = getCurrentPc();
- resetOutlineInformation();
- }
-
- // Non-materializing events use super, ie, AdvancePC, AdvanceLine and SetInlineFrame.
-
- // Materializing events are just amended to the stream.
-
- @Override
- public void visit(SetFile setFile) {
- processedEvents.add(setFile);
- }
-
- @Override
- public void visit(SetPrologueEnd setPrologueEnd) {
- processedEvents.add(setPrologueEnd);
- }
-
- @Override
- public void visit(SetEpilogueBegin setEpilogueBegin) {
- processedEvents.add(setEpilogueBegin);
- }
-
- // Local changes must force flush the PC ensuing they pertain to the correct point.
-
- @Override
- public void visit(StartLocal startLocal) {
- flushPc();
- processedEvents.add(startLocal);
- }
-
- @Override
- public void visit(EndLocal endLocal) {
- flushPc();
- processedEvents.add(endLocal);
- }
-
- @Override
- public void visit(RestartLocal restartLocal) {
- flushPc();
- processedEvents.add(restartLocal);
- }
- };
+ appView.graphLens().getOriginalMethodSignature(method.getReference()));
DexDebugEvent[] events = debugInfo.events;
- if (events.length > 0) {
- SetPositionFrame preambleFrame = getAsPreambleFrame(events[0]);
- if (preambleFrame != null) {
- // The preamble is specially identified here as it is active at method entry and thus not
- // part of the instruction stream events.
- Position position = preambleFrame.getPosition();
- Position newPosition =
- PositionUtils.remapAndAdd(position, positionRemapper, mappedPositions);
- processedEvents.add(appView.dexItemFactory().createPositionFrame(newPosition));
- }
- for (int i = (preambleFrame == null) ? 0 : 1; i < events.length; i++) {
- events[i].accept(visitor);
- }
+ for (DexDebugEvent event : events) {
+ event.accept(visitor);
+ }
+
+ // We still need to emit a preamble if we did not materialize any other instructions.
+ if (mappedPositions.isEmpty()) {
+ visitor.emitPreamblePosition();
}
EventBasedDebugInfo optimizedDebugInfo =
@@ -210,23 +249,13 @@
processedEvents.toArray(DexDebugEvent.EMPTY_ARRAY));
assert !isIdentityMapping
- || inlinedOriginalPosition.get()
+ || visitor.inlinedOriginalPosition
|| verifyIdentityMapping(debugInfo, optimizedDebugInfo);
dexCode.setDebugInfo(optimizedDebugInfo);
return mappedPositions;
}
- private SetPositionFrame getAsPreambleFrame(DexDebugEvent event) {
- SetPositionFrame positionFrame = event.asSetPositionFrame();
- if (positionFrame != null
- && positionFrame.getPosition().isSyntheticPosition()
- && positionFrame.getPosition().getLine() == 0) {
- return positionFrame;
- }
- return null;
- }
-
// This conversion *always* creates an event based debug info encoding as any non-info will
// be created as an implicit PC encoding.
private static EventBasedDebugInfo getEventBasedDebugInfo(
@@ -243,27 +272,6 @@
return debugInfo;
}
- private static Position getPositionFromPositionState(DexDebugPositionState state) {
- PositionBuilder<?, ?> positionBuilder;
- if (state.getOutlineCallee() != null) {
- OutlineCallerPositionBuilder outlineCallerPositionBuilder =
- OutlineCallerPosition.builder()
- .setOutlineCallee(state.getOutlineCallee())
- .setIsOutline(state.isOutline());
- state.getOutlineCallerPositions().forEach(outlineCallerPositionBuilder::addOutlinePosition);
- positionBuilder = outlineCallerPositionBuilder;
- } else if (state.isOutline()) {
- positionBuilder = OutlinePosition.builder();
- } else {
- positionBuilder = SourcePosition.builder().setFile(state.getCurrentFile());
- }
- return positionBuilder
- .setLine(state.getCurrentLine())
- .setMethod(state.getCurrentMethod())
- .setCallerPosition(state.getCurrentCallerPosition())
- .build();
- }
-
private static boolean verifyIdentityMapping(
EventBasedDebugInfo originalDebugInfo, EventBasedDebugInfo optimizedDebugInfo) {
assert optimizedDebugInfo.startLine == originalDebugInfo.startLine;
diff --git a/src/main/java/com/android/tools/r8/utils/positions/DexPositionToPcMappedRangeMapper.java b/src/main/java/com/android/tools/r8/utils/positions/DexPositionToPcMappedRangeMapper.java
index 890cb80..40f80b4 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/DexPositionToPcMappedRangeMapper.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/DexPositionToPcMappedRangeMapper.java
@@ -16,11 +16,6 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.Position.OutlineCallerPosition;
-import com.android.tools.r8.ir.code.Position.OutlineCallerPosition.OutlineCallerPositionBuilder;
-import com.android.tools.r8.ir.code.Position.OutlinePosition;
-import com.android.tools.r8.ir.code.Position.PositionBuilder;
-import com.android.tools.r8.ir.code.Position.SourcePosition;
import com.android.tools.r8.utils.IntBox;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.positions.PositionToMappedRangeMapper.PcBasedDebugInfoRecorder;
@@ -58,7 +53,7 @@
if (firstDefaultEventPc.get() < 0) {
firstDefaultEventPc.set(getCurrentPc());
}
- Position currentPosition = getPositionFromPositionState(this);
+ Position currentPosition = getPosition();
if (lastPosition.getSecond() != null) {
remapAndAddForPc(
pcBasedDebugInfo,
@@ -70,7 +65,6 @@
}
lastPosition.setFirst(getCurrentPc());
lastPosition.setSecond(currentPosition);
- resetOutlineInformation();
}
};
@@ -104,18 +98,8 @@
Pair<Position, Position> remappedPosition = remapper.createRemappedPosition(position);
Position oldPosition = remappedPosition.getFirst();
for (int currentPc = startPc; currentPc < endPc; currentPc++) {
- boolean firstEntry = currentPc == startPc;
mappedPositions.add(
- new MappedPosition(
- oldPosition.getMethod(),
- oldPosition.getLine(),
- oldPosition.getCallerPosition(),
- debugInfoProvider.getPcEncoding(currentPc),
- // Outline info is placed exactly on the positions that relate to it so we should
- // only emit it for the first entry.
- firstEntry && oldPosition.isOutline(),
- firstEntry ? oldPosition.getOutlineCallee() : null,
- firstEntry ? oldPosition.getOutlinePositions() : null));
+ new MappedPosition(oldPosition, debugInfoProvider.getPcEncoding(currentPc)));
}
}
@@ -135,27 +119,6 @@
return debugInfo;
}
- private static Position getPositionFromPositionState(DexDebugPositionState state) {
- PositionBuilder<?, ?> positionBuilder;
- if (state.getOutlineCallee() != null) {
- OutlineCallerPositionBuilder outlineCallerPositionBuilder =
- OutlineCallerPosition.builder()
- .setOutlineCallee(state.getOutlineCallee())
- .setIsOutline(state.isOutline());
- state.getOutlineCallerPositions().forEach(outlineCallerPositionBuilder::addOutlinePosition);
- positionBuilder = outlineCallerPositionBuilder;
- } else if (state.isOutline()) {
- positionBuilder = OutlinePosition.builder();
- } else {
- positionBuilder = SourcePosition.builder().setFile(state.getCurrentFile());
- }
- return positionBuilder
- .setLine(state.getCurrentLine())
- .setMethod(state.getCurrentMethod())
- .setCallerPosition(state.getCurrentCallerPosition())
- .build();
- }
-
private static boolean verifyIdentityMapping(
EventBasedDebugInfo originalDebugInfo, EventBasedDebugInfo optimizedDebugInfo) {
assert optimizedDebugInfo.startLine == originalDebugInfo.startLine;
diff --git a/src/main/java/com/android/tools/r8/utils/positions/MappedPosition.java b/src/main/java/com/android/tools/r8/utils/positions/MappedPosition.java
index a156c80..3b29392 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/MappedPosition.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/MappedPosition.java
@@ -4,66 +4,23 @@
package com.android.tools.r8.utils.positions;
-import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.utils.Int2StructuralItemArrayMap;
public class MappedPosition {
- private final DexMethod method;
- private final int originalLine;
- private final Position caller;
private final int obfuscatedLine;
- private final boolean isOutline;
- private final DexMethod outlineCallee;
- private final Int2StructuralItemArrayMap<Position> outlinePositions;
+ private final Position position;
- public MappedPosition(
- DexMethod method,
- int originalLine,
- Position caller,
- int obfuscatedLine,
- boolean isOutline,
- DexMethod outlineCallee,
- Int2StructuralItemArrayMap<Position> outlinePositions) {
- this.method = method;
- this.originalLine = originalLine;
- this.caller = caller;
+ public MappedPosition(Position position, int obfuscatedLine) {
+ this.position = position;
this.obfuscatedLine = obfuscatedLine;
- this.isOutline = isOutline;
- this.outlineCallee = outlineCallee;
- this.outlinePositions = outlinePositions;
- }
-
- public DexMethod getMethod() {
- return method;
- }
-
- public int getOriginalLine() {
- return originalLine;
- }
-
- public Position getCaller() {
- return caller;
}
public int getObfuscatedLine() {
return obfuscatedLine;
}
- public boolean isOutline() {
- return isOutline;
- }
-
- public DexMethod getOutlineCallee() {
- return outlineCallee;
- }
-
- public Int2StructuralItemArrayMap<Position> getOutlinePositions() {
- return outlinePositions;
- }
-
- public boolean isOutlineCaller() {
- return outlineCallee != null;
+ public Position getPosition() {
+ return position;
}
}
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 13f013e..2915d14 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
@@ -248,7 +248,7 @@
m, key -> MethodSignature.fromDexMethod(m, m.holder != clazz.getType()));
// Check if mapped position is an outline
- DexMethod outlineMethod = getOutlineMethod(mappedPositions.get(0));
+ DexMethod outlineMethod = getOutlineMethod(mappedPositions.get(0).getPosition());
if (outlineMethod != null) {
outlinesToFix
.computeIfAbsent(
@@ -261,38 +261,48 @@
// Update memberNaming with the collected positions, merging multiple positions into a
// single region whenever possible.
for (int i = 0; i < mappedPositions.size(); /* updated in body */ ) {
- MappedPosition firstPosition = mappedPositions.get(i);
+ MappedPosition firstMappedPosition = mappedPositions.get(i);
int j = i + 1;
- MappedPosition lastPosition = firstPosition;
+ MappedPosition lastMappedPosition = firstMappedPosition;
MappedPositionRange mappedPositionRange = MappedPositionRange.SINGLE_LINE;
for (; j < mappedPositions.size(); j++) {
// Break if this position cannot be merged with lastPosition.
- MappedPosition currentPosition = mappedPositions.get(j);
+ MappedPosition currentMappedPosition = mappedPositions.get(j);
mappedPositionRange =
- mappedPositionRange.canAddNextMappingToRange(lastPosition, currentPosition);
+ mappedPositionRange.canAddNextMappingToRange(
+ lastMappedPosition, currentMappedPosition);
// Note that currentPosition.caller and lastPosition.class must be deep-compared since
// multiple inlining passes lose the canonical property of the positions.
+ Position currentPosition = currentMappedPosition.getPosition();
+ Position lastPosition = lastMappedPosition.getPosition();
if (currentPosition.getMethod() != lastPosition.getMethod()
|| mappedPositionRange.isOutOfRange()
- || !Objects.equals(currentPosition.getCaller(), lastPosition.getCaller())
+ || !Objects.equals(
+ currentPosition.getCallerPosition(), lastPosition.getCallerPosition())
// Break when we see a mapped outline
|| currentPosition.getOutlineCallee() != null
// Ensure that we break when we start iterating with an outline caller again.
- || firstPosition.getOutlineCallee() != null) {
+ || firstMappedPosition.getPosition().getOutlineCallee() != null) {
break;
}
// The mapped positions are not guaranteed to be in order, so maintain first and last
// position.
- if (firstPosition.getObfuscatedLine() > currentPosition.getObfuscatedLine()) {
- firstPosition = currentPosition;
+ if (firstMappedPosition.getObfuscatedLine() > currentMappedPosition.getObfuscatedLine()) {
+ firstMappedPosition = currentMappedPosition;
}
- if (lastPosition.getObfuscatedLine() < currentPosition.getObfuscatedLine()) {
- lastPosition = currentPosition;
+ if (lastMappedPosition.getObfuscatedLine() < currentMappedPosition.getObfuscatedLine()) {
+ lastMappedPosition = currentMappedPosition;
}
}
Range obfuscatedRange =
nonCardinalRangeCache.get(
- firstPosition.getObfuscatedLine(), lastPosition.getObfuscatedLine());
+ firstMappedPosition.getObfuscatedLine(), lastMappedPosition.getObfuscatedLine());
+
+ Position firstPosition = firstMappedPosition.getPosition();
+ Position lastPosition = lastMappedPosition.getPosition();
+
+ Range originalRange =
+ nonCardinalRangeCache.get(firstPosition.getLine(), lastPosition.getLine());
MappedRange lastMappedRange =
getMappedRangesForPosition(
@@ -302,9 +312,8 @@
firstPosition.getMethod(),
residualSignature,
obfuscatedRange,
- nonCardinalRangeCache.get(
- firstPosition.getOriginalLine(), lastPosition.getOriginalLine()),
- firstPosition.getCaller(),
+ originalRange,
+ firstPosition.getCallerPosition(),
prunedInlinedClasses,
cardinalRangeCache);
methodSpecificMappingInformation.consume(
@@ -407,11 +416,11 @@
return lastMappedRange;
}
- private DexMethod getOutlineMethod(MappedPosition mappedPosition) {
+ private DexMethod getOutlineMethod(Position mappedPosition) {
if (mappedPosition.isOutline()) {
return mappedPosition.getMethod();
}
- Position caller = mappedPosition.getCaller();
+ Position caller = mappedPosition.getCallerPosition();
if (caller == null) {
return null;
}
@@ -500,8 +509,9 @@
// is OK since retrace(a(:7)) = 42, however, the following is not OK:
// 1:10:void foo():42:43 -> a
// since retrace(a(:7)) = 49, which is not correct.
- boolean hasSameRightHandSide =
- lastPosition.getOriginalLine() == currentPosition.getOriginalLine();
+ int currentOriginalLine = currentPosition.getPosition().getLine();
+ int lastOriginalLine = lastPosition.getPosition().getLine();
+ boolean hasSameRightHandSide = lastOriginalLine == currentOriginalLine;
if (hasSameRightHandSide) {
if (isSameDelta()) {
return OUT_OF_RANGE;
@@ -515,7 +525,7 @@
return OUT_OF_RANGE;
}
boolean sameDelta =
- currentPosition.getOriginalLine() - lastPosition.getOriginalLine()
+ currentOriginalLine - lastOriginalLine
== currentPosition.getObfuscatedLine() - lastPosition.getObfuscatedLine();
return sameDelta ? SAME_DELTA : OUT_OF_RANGE;
}
@@ -569,7 +579,7 @@
private int getMinifiedLinePosition(
int originalPosition, List<MappedPosition> mappedPositions) {
for (MappedPosition mappedPosition : mappedPositions) {
- if (mappedPosition.getOriginalLine() == originalPosition) {
+ if (mappedPosition.getPosition().getLine() == originalPosition) {
return mappedPosition.getObfuscatedLine();
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/positions/PositionUtils.java b/src/main/java/com/android/tools/r8/utils/positions/PositionUtils.java
index b7247e7..0b0b20a 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/PositionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/PositionUtils.java
@@ -24,15 +24,7 @@
Pair<Position, Position> remappedPosition = remapper.createRemappedPosition(position);
Position oldPosition = remappedPosition.getFirst();
Position newPosition = remappedPosition.getSecond();
- mappedPositions.add(
- new MappedPosition(
- oldPosition.getMethod(),
- oldPosition.getLine(),
- oldPosition.getCallerPosition(),
- newPosition.getLine(),
- oldPosition.isOutline(),
- oldPosition.getOutlineCallee(),
- oldPosition.getOutlinePositions()));
+ mappedPositions.add(new MappedPosition(oldPosition, newPosition.getLine()));
return newPosition;
}
diff --git a/src/test/examplesJava9/varhandle/InstanceObjectField.java b/src/test/examplesJava9/varhandle/InstanceObjectField.java
index e7b4970..1b4a4c9 100644
--- a/src/test/examplesJava9/varhandle/InstanceObjectField.java
+++ b/src/test/examplesJava9/varhandle/InstanceObjectField.java
@@ -5,19 +5,160 @@
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
+import java.lang.invoke.WrongMethodTypeException;
public class InstanceObjectField {
private Object field;
+ public static class A {
+
+ private final int i;
+
+ public A(int i) {
+ this.i = i;
+ }
+
+ public String toString() {
+ return new StringBuilder().append("A(").append(i).append(")").toString();
+ }
+ }
+
public static void testSet(VarHandle varHandle) {
- System.out.println("testGet");
+ System.out.println("testSet");
InstanceObjectField instance = new InstanceObjectField();
System.out.println(varHandle.get(instance));
- varHandle.set(instance, 1);
+ A a1 = new A(1);
+ varHandle.set(instance, a1);
System.out.println(varHandle.get(instance));
+ System.out.println(varHandle.get(instance) == a1);
+ A a2 = new A(2);
+ varHandle.set(instance, a2);
+ System.out.println(varHandle.get(instance));
+ System.out.println(varHandle.get(instance) == a2);
+
+ Object o;
+ {
+ int i;
+ varHandle.set(instance, 1);
+ System.out.println(varHandle.get(instance));
+ System.out.println((int) varHandle.get(instance));
+ o = varHandle.get(instance);
+ System.out.println(o instanceof Integer);
+ i = (int) varHandle.get(instance);
+ System.out.println(i == 1);
+ varHandle.set(instance, Integer.valueOf(2));
+ System.out.println(varHandle.get(instance));
+ System.out.println((int) varHandle.get(instance));
+ o = varHandle.get(instance);
+ System.out.println(o instanceof Integer);
+ i = (int) varHandle.get(instance);
+ System.out.println(i == 2);
+ }
+ {
+ long l;
+ varHandle.set(instance, 3L);
+ System.out.println(varHandle.get(instance));
+ System.out.println((long) varHandle.get(instance));
+ o = varHandle.get(instance);
+ System.out.println(o instanceof Long);
+ l = (long) varHandle.get(instance);
+ System.out.println(l == 3L);
+ varHandle.set(instance, Long.valueOf(4L));
+ System.out.println(varHandle.get(instance));
+ System.out.println((long) varHandle.get(instance));
+ o = varHandle.get(instance);
+ System.out.println(o instanceof Long);
+ l = (long) varHandle.get(instance);
+ System.out.println(l == 4L);
+ }
+ {
+ byte b;
+ varHandle.set(instance, (byte) 5);
+ System.out.println(varHandle.get(instance));
+ System.out.println((byte) varHandle.get(instance));
+ o = varHandle.get(instance);
+ System.out.println(o instanceof Byte);
+ b = (byte) varHandle.get(instance);
+ System.out.println(b == (byte) 5);
+ varHandle.set(instance, Byte.valueOf((byte) 6));
+ System.out.println(varHandle.get(instance));
+ System.out.println((byte) varHandle.get(instance));
+ o = varHandle.get(instance);
+ System.out.println(o instanceof Byte);
+ b = (byte) varHandle.get(instance);
+ System.out.println(b == 6);
+ }
+ {
+ short s;
+ varHandle.set(instance, (short) 7);
+ System.out.println(varHandle.get(instance));
+ System.out.println((short) varHandle.get(instance));
+ o = varHandle.get(instance);
+ System.out.println(o instanceof Short);
+ s = (short) varHandle.get(instance);
+ System.out.println(s == (short) 7);
+ varHandle.set(instance, Short.valueOf((short) 8));
+ System.out.println(varHandle.get(instance));
+ System.out.println((short) varHandle.get(instance));
+ o = varHandle.get(instance);
+ System.out.println(o instanceof Short);
+ s = (short) varHandle.get(instance);
+ System.out.println(s == 8);
+ }
+ {
+ float f;
+ varHandle.set(instance, (float) 9.0f);
+ System.out.println(varHandle.get(instance));
+ System.out.println((float) varHandle.get(instance));
+ o = varHandle.get(instance);
+ System.out.println(o instanceof Float);
+ f = (float) varHandle.get(instance);
+ System.out.println(f == (float) 9.0f);
+ varHandle.set(instance, Float.valueOf(10.0f));
+ System.out.println(varHandle.get(instance));
+ System.out.println((float) varHandle.get(instance));
+ o = varHandle.get(instance);
+ System.out.println(o instanceof Float);
+ f = (float) varHandle.get(instance);
+ System.out.println(f == 10.0f);
+ }
+ {
+ double d;
+ varHandle.set(instance, (double) 11.0);
+ System.out.println(varHandle.get(instance));
+ System.out.println((double) varHandle.get(instance));
+ o = varHandle.get(instance);
+ System.out.println(o instanceof Double);
+ d = (double) varHandle.get(instance);
+ System.out.println(d == (double) 11.0);
+ varHandle.set(instance, Double.valueOf(12.0));
+ System.out.println(varHandle.get(instance));
+ System.out.println((double) varHandle.get(instance));
+ o = varHandle.get(instance);
+ System.out.println(o instanceof Double);
+ d = (double) varHandle.get(instance);
+ System.out.println(d == 12.0);
+ }
+ {
+ char c;
+ varHandle.set(instance, 'A');
+ System.out.println(varHandle.get(instance));
+ System.out.println((char) varHandle.get(instance));
+ o = varHandle.get(instance);
+ System.out.println(o instanceof Character);
+ c = (char) varHandle.get(instance);
+ System.out.println(c == 'A');
+ varHandle.set(instance, Character.valueOf('B'));
+ System.out.println(varHandle.get(instance));
+ System.out.println((char) varHandle.get(instance));
+ o = varHandle.get(instance);
+ System.out.println(o instanceof Character);
+ c = (char) varHandle.get(instance);
+ System.out.println(c == 'B');
+ }
}
public static void testCompareAndSet(VarHandle varHandle) {
@@ -25,10 +166,106 @@
InstanceObjectField instance = new InstanceObjectField();
- varHandle.compareAndSet(instance, 0, 1);
+ A a1 = new A(1);
+ varHandle.compareAndSet(instance, 0, a1);
System.out.println(varHandle.get(instance));
- varHandle.compareAndSet(instance, null, 1);
+ varHandle.compareAndSet(instance, null, a1);
System.out.println(varHandle.get(instance));
+ System.out.println(varHandle.get(instance) == a1);
+ A a2 = new A(2);
+ varHandle.compareAndSet(instance, a1, a2);
+ System.out.println(varHandle.get(instance));
+ System.out.println(varHandle.get(instance) == a2);
+
+ varHandle.compareAndSet(instance, a2, 1);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, Integer.valueOf(1), 2);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, Integer.valueOf(2), Integer.valueOf(3));
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, Integer.valueOf(3), 4);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, 4L, 5);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, (byte) 4, 5);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, (short) 4, 5);
+ System.out.println(varHandle.get(instance));
+
+ varHandle.compareAndSet(instance, 4, 5L);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, Long.valueOf(5), 6L);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, Long.valueOf(6), Long.valueOf(7));
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, Long.valueOf(7), 8L);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, 8, 9L);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, (byte) 8, 9);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, (short) 8, 9);
+ System.out.println(varHandle.get(instance));
+ System.out.println(varHandle.get(instance) == a1);
+ varHandle.compareAndSet(instance, a1, a2);
+ System.out.println(varHandle.get(instance));
+ System.out.println(varHandle.get(instance) == a2);
+
+ varHandle.compareAndSet(instance, a2, 1);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, Integer.valueOf(1), 2);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, Integer.valueOf(2), Integer.valueOf(3));
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, Integer.valueOf(3), 4);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, 4L, 5);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, (byte) 4, 5);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, (short) 4, 5);
+ System.out.println(varHandle.get(instance));
+
+ varHandle.compareAndSet(instance, 4, 5L);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, Long.valueOf(5), 6L);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, Long.valueOf(6), Long.valueOf(7));
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, Long.valueOf(7), 8L);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, 8, 9L);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, (byte) 8, 9);
+ System.out.println(varHandle.get(instance));
+ varHandle.compareAndSet(instance, (short) 8, 9);
+ System.out.println(varHandle.get(instance));
+ }
+
+ public static void testReturnValueClassCastException(VarHandle varHandle) {
+ System.out.println("testReturnValueClassCastException");
+
+ InstanceObjectField instance = new InstanceObjectField();
+ A a = new A(1);
+
+ varHandle.set(instance, a);
+ try {
+ System.out.println((int) varHandle.get(instance));
+ System.out.println("Expected ClassCastException");
+ } catch (ClassCastException e) {
+ System.out.println("Reference implementation");
+ } catch (WrongMethodTypeException e) {
+ System.out.println("Art implementation");
+ }
+ varHandle.set(instance, a);
+ try {
+ System.out.println((int) (Integer) (int) varHandle.get(instance));
+ System.out.println("Expected ClassCastException");
+ } catch (ClassCastException e) {
+ System.out.println("Reference implementation");
+ } catch (WrongMethodTypeException e) {
+ System.out.println("Art implementation");
+ }
}
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
@@ -36,5 +273,6 @@
MethodHandles.lookup().findVarHandle(InstanceObjectField.class, "field", Object.class);
testSet(varHandle);
testCompareAndSet(varHandle);
+ testReturnValueClassCastException(varHandle);
}
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ClassValueTest.java b/src/test/java/com/android/tools/r8/apimodel/ClassValueTest.java
new file mode 100644
index 0000000..ad9ea40
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ClassValueTest.java
@@ -0,0 +1,224 @@
+// 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.apimodel;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+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.Assume.assumeTrue;
+
+import com.android.tools.r8.ProguardVersion;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+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 ClassValueTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static List<Object[]> data() {
+ return buildParameters(getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ private static final String EXPECTED_OUTPUT = StringUtils.lines(TestClass.class.getTypeName());
+
+ private void computeValuePresent(CodeInspector inspector) {
+ assertThat(
+ inspector.clazz(ClassValueSub.class).uniqueMethodWithOriginalName("computeValue"),
+ isPresent());
+ }
+
+ private void computeValueAbsent(CodeInspector inspector) {
+ assertThat(
+ inspector.clazz(ClassValueSub.class).uniqueMethodWithOriginalName("computeValue"),
+ isAbsent());
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForRuntime(parameters)
+ .addInnerClasses(getClass())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::computeValuePresent)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertFailureWithErrorThatThrows(NoClassDefFoundError.class);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .addDontWarn(ClassValue.class)
+ .compile()
+ .inspect(this::computeValueAbsent)
+ .run(parameters.getRuntime(), TestClass.class)
+ .applyIf(
+ parameters.isCfRuntime(),
+ r -> r.assertFailureWithErrorThatThrows(AbstractMethodError.class),
+ r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
+ }
+
+ @Test
+ public void testProguard() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForProguard(ProguardVersion.V7_0_0)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ // ProGuard warns about the inner class attributes referring to this outer class.
+ .addDontWarn(this.getClass().getTypeName())
+ // ProGuard also warns about ClassValueSub not having method get.
+ .addDontWarn(ClassValueSub.class.getTypeName())
+ .compile()
+ .inspect(this::computeValueAbsent)
+ .run(parameters.getRuntime(), TestClass.class)
+ .applyIf(
+ parameters.isCfRuntime(),
+ r -> r.assertFailureWithErrorThatThrows(AbstractMethodError.class),
+ r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
+ }
+
+ @Test
+ public void testR8KeepExtendsMissingType() throws Exception {
+ testForR8(parameters.getBackend())
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .addDontWarn(ClassValue.class)
+ // Try to keep computeValue on classes extending unknown type.
+ .addKeepRules("-keep class * extends " + ClassValue.class.getTypeName() + " { *; }")
+ .allowUnusedProguardConfigurationRules()
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertInfoThatMatches(
+ diagnosticMessage(
+ containsString(
+ "Proguard configuration rule does not match anything: "
+ + "`-keep class * extends "
+ + ClassValue.class.getTypeName()
+ + " {"))))
+ .inspect(this::computeValueAbsent)
+ .run(parameters.getRuntime(), TestClass.class)
+ .applyIf(
+ parameters.isCfRuntime(),
+ r -> r.assertFailureWithErrorThatThrows(AbstractMethodError.class),
+ r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
+ }
+
+ @Test
+ public void testProguardKeepExtendsMissingTypeProguard() throws Exception {
+ assumeTrue(parameters.isCfRuntime() && parameters.asCfRuntime().getVm() == CfVm.JDK11);
+ testForProguard(ProguardVersion.V7_0_0)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ // ProGuard warns about the inner class attributes referring to this outer class.
+ .addDontWarn(this.getClass().getTypeName())
+ // Just -dontwarn on ClassValue is not sufficient. ProGuard also warns about ClassValueSub
+ // not having method get.
+ .addDontWarn(ClassValueSub.class.getTypeName())
+ // Try to keep computeValue on classes extending unknown type.
+ .addKeepRules("-keep class * extends " + ClassValue.class.getTypeName() + " { *; }")
+ // TODO(b/261971620): Support extends matching names of missing classes.
+ .compile()
+ .inspect(this::computeValueAbsent)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertFailureWithErrorThatThrows(AbstractMethodError.class);
+ }
+
+ @Test
+ public void testR8KeepConcreteMethod() throws Exception {
+ for (String dontWarn :
+ ImmutableList.of(ClassValue.class.getTypeName(), ClassValueSub.class.getTypeName())) {
+ testForR8(parameters.getBackend())
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .addDontWarn(dontWarn)
+ .addKeepRules(
+ "-keep class "
+ + ClassValueSub.class.getTypeName()
+ + " { ** computeValue(java.lang.Class); }")
+ .compile()
+ .inspect(this::computeValuePresent)
+ .run(parameters.getRuntime(), TestClass.class)
+ .applyIf(
+ parameters.isCfRuntime(),
+ r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT),
+ r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
+ }
+ }
+
+ @Test
+ public void testProguardKeepConcreteMethod() throws Exception {
+ assumeTrue(parameters.isCfRuntime() && parameters.asCfRuntime().getVm() == CfVm.JDK11);
+ testForProguard(ProguardVersion.V7_0_0)
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ // ProGuard warns about the inner class attributes referring to this outer class.
+ .addDontWarn(this.getClass().getTypeName())
+ // Just -dontwarn on ClassValue is not sufficient. ProGuard also warns about ClassValueSub
+ // not having method get.
+ .addDontWarn(ClassValueSub.class.getTypeName())
+ .addKeepRules(
+ "-keep class "
+ + ClassValueSub.class.getTypeName()
+ + " { ** computeValue(java.lang.Class); }")
+ .compile()
+ .inspect(this::computeValuePresent)
+ .run(parameters.getRuntime(), TestClass.class)
+ .applyIf(
+ parameters.isCfRuntime(),
+ r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT),
+ r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
+ }
+
+ static class ClassValueSub extends ClassValue<Object> {
+ @Override
+ protected Object computeValue(Class<?> clazz) {
+ return clazz.getTypeName();
+ }
+ }
+
+ static class TestClass {
+ static ClassValueSub classValueSub = new ClassValueSub();
+
+ public static void main(String[] args) {
+ System.out.println(classValueSub.get(TestClass.class));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/stackmap/UndefinedTypesAssignmentTest.java b/src/test/java/com/android/tools/r8/cf/stackmap/UndefinedTypesAssignmentTest.java
new file mode 100644
index 0000000..9e8c086e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/stackmap/UndefinedTypesAssignmentTest.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.cf.stackmap;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.UnverifiableCfCodeDiagnostic;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/** Regression test for b/b/261967650 */
+@RunWith(Parameterized.class)
+public class UndefinedTypesAssignmentTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("Hello, world");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withDefaultRuntimes()
+ .withApiLevel(AndroidApiLevel.B)
+ .enableApiLevelsForCf()
+ .build();
+ }
+
+ public UndefinedTypesAssignmentTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ testForD8(parameters.getBackend())
+ .addProgramClasses(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addRunClasspathClasses(I.class, A.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class)
+ .addDontWarn(I.class, A.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .allowDiagnosticWarningMessages()
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics
+ .assertOnlyWarnings()
+ .assertWarningsMatch(diagnosticType(UnverifiableCfCodeDiagnostic.class)))
+ .addRunClasspathClasses(I.class, A.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ interface I {}
+
+ static class A implements I {}
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ I i = null;
+ try {
+ i = new A();
+ System.out.println("Hello, world");
+ } catch (Exception e) {
+ System.out.println(i);
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringArrayOfIntTest.java b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringArrayOfIntTest.java
index af62516..d7ca20f 100644
--- a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringArrayOfIntTest.java
+++ b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringArrayOfIntTest.java
@@ -6,6 +6,8 @@
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;
@@ -23,12 +25,12 @@
}
@Override
- protected String getJarEntry() {
- return JAR_ENTRY;
+ protected List<String> getJarEntries() {
+ return ImmutableList.of(JAR_ENTRY);
}
@Override
- protected String getExpectedOutput() {
+ protected String getExpectedOutputForReferenceImplementation() {
return EXPECTED_OUTPUT;
}
}
diff --git a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringArrayOfLongTest.java b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringArrayOfLongTest.java
index 46e130f..c3c81da 100644
--- a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringArrayOfLongTest.java
+++ b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringArrayOfLongTest.java
@@ -6,6 +6,8 @@
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;
@@ -23,12 +25,12 @@
}
@Override
- protected String getJarEntry() {
- return JAR_ENTRY;
+ protected List<String> getJarEntries() {
+ return ImmutableList.of(JAR_ENTRY);
}
@Override
- protected String getExpectedOutput() {
+ protected String getExpectedOutputForReferenceImplementation() {
return EXPECTED_OUTPUT;
}
}
diff --git a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceIntFieldTest.java b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceIntFieldTest.java
index 9ae8ecd..7cfc0e5 100644
--- a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceIntFieldTest.java
+++ b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceIntFieldTest.java
@@ -6,6 +6,8 @@
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;
@@ -76,12 +78,12 @@
}
@Override
- protected String getJarEntry() {
- return JAR_ENTRY;
+ protected List<String> getJarEntries() {
+ return ImmutableList.of(JAR_ENTRY);
}
@Override
- protected String getExpectedOutput() {
+ protected String getExpectedOutputForReferenceImplementation() {
return EXPECTED_OUTPUT;
}
diff --git a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceLongFieldTest.java b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceLongFieldTest.java
index c50db54..227cc76 100644
--- a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceLongFieldTest.java
+++ b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceLongFieldTest.java
@@ -6,6 +6,8 @@
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;
@@ -75,12 +77,12 @@
}
@Override
- protected String getJarEntry() {
- return JAR_ENTRY;
+ protected List<String> getJarEntries() {
+ return ImmutableList.of(JAR_ENTRY);
}
@Override
- protected String getExpectedOutput() {
+ protected String getExpectedOutputForReferenceImplementation() {
return EXPECTED_OUTPUT;
}
diff --git a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceObjectFieldTest.java b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceObjectFieldTest.java
index b9b2b8e..ac43573 100644
--- a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceObjectFieldTest.java
+++ b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceObjectFieldTest.java
@@ -6,6 +6,8 @@
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;
@@ -13,9 +15,111 @@
public class VarHandleDesugaringInstanceObjectFieldTest extends VarHandleDesugaringTestBase {
private static final String EXPECTED_OUTPUT =
- StringUtils.lines("testGet", "null", "1", "testCompareAndSet", "null", "1");
+ StringUtils.lines(
+ "testSet",
+ "null",
+ "A(1)",
+ "true",
+ "A(2)",
+ "true",
+ "1",
+ "1",
+ "true",
+ "true",
+ "2",
+ "2",
+ "true",
+ "true",
+ "3",
+ "3",
+ "true",
+ "true",
+ "4",
+ "4",
+ "true",
+ "true",
+ "5",
+ "5",
+ "true",
+ "true",
+ "6",
+ "6",
+ "true",
+ "true",
+ "7",
+ "7",
+ "true",
+ "true",
+ "8",
+ "8",
+ "true",
+ "true",
+ "9.0",
+ "9.0",
+ "true",
+ "true",
+ "10.0",
+ "10.0",
+ "true",
+ "true",
+ "11.0",
+ "11.0",
+ "true",
+ "true",
+ "12.0",
+ "12.0",
+ "true",
+ "true",
+ "A",
+ "A",
+ "true",
+ "true",
+ "B",
+ "B",
+ "true",
+ "true",
+ "testCompareAndSet",
+ "null",
+ "A(1)",
+ "true",
+ "A(2)",
+ "true",
+ "1",
+ "2",
+ "3",
+ "4",
+ "4",
+ "4",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "8",
+ "8",
+ "8",
+ "false",
+ "8",
+ "false",
+ "8",
+ "8",
+ "8",
+ "8",
+ "8",
+ "8",
+ "8",
+ "8",
+ "8",
+ "8",
+ "8",
+ "8",
+ "8",
+ "8",
+ "testReturnValueClassCastException");
private static final String MAIN_CLASS = VarHandle.InstanceObjectField.typeName();
- private static final String JAR_ENTRY = "varhandle/InstanceObjectField.class";
+ private static final List<String> JAR_ENTRIES =
+ ImmutableList.of(
+ "varhandle/InstanceObjectField.class", "varhandle/InstanceObjectField$A.class");
@Override
protected String getMainClass() {
@@ -28,12 +132,24 @@
}
@Override
- protected String getJarEntry() {
- return JAR_ENTRY;
+ protected List<String> getJarEntries() {
+ return JAR_ENTRIES;
}
@Override
- protected String getExpectedOutput() {
- return EXPECTED_OUTPUT;
+ protected String getExpectedOutputForReferenceImplementation() {
+ return StringUtils.lines(
+ EXPECTED_OUTPUT.trim(), "Reference implementation", "Reference implementation");
+ }
+
+ @Override
+ protected String getExpectedOutputForArtImplementation() {
+ assert parameters.isDexRuntime();
+ return StringUtils.lines(EXPECTED_OUTPUT.trim(), "Art implementation", "Art implementation");
+ }
+
+ @Override
+ protected boolean getTestWithDesugaring() {
+ return true;
}
}
diff --git a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceStringFieldTest.java b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceStringFieldTest.java
index dfd32cf..f33f52c 100644
--- a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceStringFieldTest.java
+++ b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringInstanceStringFieldTest.java
@@ -6,6 +6,8 @@
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;
@@ -28,12 +30,12 @@
}
@Override
- protected String getJarEntry() {
- return JAR_ENTRY;
+ protected List<String> getJarEntries() {
+ return ImmutableList.of(JAR_ENTRY);
}
@Override
- protected String getExpectedOutput() {
+ protected String getExpectedOutputForReferenceImplementation() {
return EXPECTED_OUTPUT;
}
}
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 6a6c9d5..6a73d66 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
@@ -4,8 +4,12 @@
package com.android.tools.r8.cf.varhandle;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.JdkClassFileProvider;
import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -14,8 +18,20 @@
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;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.IntBox;
import com.android.tools.r8.utils.ZipUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.util.List;
+import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -45,9 +61,13 @@
return "";
}
- protected abstract String getJarEntry();
+ protected abstract List<String> getJarEntries();
- protected abstract String getExpectedOutput();
+ protected abstract String getExpectedOutputForReferenceImplementation();
+
+ protected String getExpectedOutputForArtImplementation() {
+ return getExpectedOutputForReferenceImplementation();
+ }
// TODO(b/247076137): Remove this when all tests can run with desugaring.
protected boolean getTestWithDesugaring() {
@@ -60,29 +80,129 @@
testForJvm()
.addProgramFiles(VarHandle.jar())
.run(parameters.getRuntime(), getMainClass())
- .assertSuccessWithOutput(getExpectedOutput());
+ .assertSuccessWithOutput(getExpectedOutputForReferenceImplementation());
}
+ private List<byte[]> getProgramClassFileData() {
+ return getJarEntries().stream()
+ .map(
+ entry -> {
+ try {
+ return ZipUtils.readSingleEntry(VarHandle.jar(), entry);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ })
+ .collect(Collectors.toList());
+ }
+
+ private boolean willDesugarVarHandle() {
+ return parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.T);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ IntBox unsafeCompareAndSwapInt = new IntBox();
+ IntBox unsafeCompareAndSwapLong = new IntBox();
+ IntBox unsafeCompareAndSwapObject = new IntBox();
+ DexString compareAndSwapInt = inspector.getFactory().createString("compareAndSwapInt");
+ DexString compareAndSwapLong = inspector.getFactory().createString("compareAndSwapLong");
+ DexString compareAndSwapObject = inspector.getFactory().createString("compareAndSwapObject");
+ // Right now we only expect one backport coming out of DesugarVarHandle - the backport with
+ // forwarding of Unsafe.compareAndSwapObject.
+ MethodReference firstBackportFromDesugarVarHandle =
+ SyntheticItemsTestUtils.syntheticBackportWithForwardingMethod(
+ Reference.classFromDescriptor(DexItemFactory.desugarVarHandleDescriptorString),
+ 0,
+ Reference.method(
+ Reference.classFromDescriptor("Lsun/misc/Unsafe;"),
+ "compareAndSwapObject",
+ ImmutableList.of(
+ Reference.typeFromDescriptor("Ljava/lang/Object;"),
+ Reference.LONG,
+ Reference.typeFromDescriptor("Ljava/lang/Object;"),
+ Reference.typeFromDescriptor("Ljava/lang/Object;")),
+ Reference.BOOL));
+ inspector.forAllClasses(
+ clazz -> {
+ clazz.forAllMethods(
+ method -> {
+ method
+ .instructions()
+ .forEach(
+ instruction -> {
+ if (instruction.isInvoke()) {
+ DexMethod target = instruction.getMethod();
+ if (target.getHolderType() == inspector.getFactory().unsafeType) {
+ if (target.getName() == compareAndSwapInt
+ || target.getName() == compareAndSwapLong) {
+ // All compareAndSwapInt and compareAndSwapLong stay on
+ // DesugarVarHandle.
+ assertSame(
+ clazz.getDexProgramClass().getType(),
+ inspector.getFactory().desugarVarHandleType);
+ unsafeCompareAndSwapInt.incrementIf(
+ target.getName() == compareAndSwapInt);
+ unsafeCompareAndSwapLong.incrementIf(
+ target.getName() == compareAndSwapLong);
+ } else if (target.getName() == compareAndSwapObject) {
+ // compareAndSwapObject is not on DesugarVarHandle - it must be
+ // backported.
+ assertNotSame(
+ clazz.getDexProgramClass().getType(),
+ inspector.getFactory().desugarVarHandleType);
+ assertEquals(
+ clazz.getFinalReference(),
+ firstBackportFromDesugarVarHandle.getHolderClass());
+ assertEquals(1, clazz.allMethods().size());
+ assertEquals(
+ firstBackportFromDesugarVarHandle,
+ clazz.allMethods().iterator().next().getFinalReference());
+ unsafeCompareAndSwapObject.increment();
+ }
+ }
+ }
+ });
+ });
+ });
+ if (willDesugarVarHandle()) {
+ assertEquals(2, unsafeCompareAndSwapInt.get());
+ assertEquals(3, unsafeCompareAndSwapLong.get());
+ assertEquals(1, unsafeCompareAndSwapObject.get());
+ } else {
+ assertEquals(0, unsafeCompareAndSwapInt.get());
+ assertEquals(0, unsafeCompareAndSwapLong.get());
+ assertEquals(0, unsafeCompareAndSwapObject.get());
+ }
+ }
+
+ // TODO(b/247076137: Also turn on VarHandle desugaring for R8 tests.
@Test
public void testD8() throws Throwable {
assumeTrue(parameters.isDexRuntime());
if (getTestWithDesugaring()) {
testForD8(parameters.getBackend())
- .addProgramClassFileData(ZipUtils.readSingleEntry(VarHandle.jar(), getJarEntry()))
+ .addProgramClassFileData(getProgramClassFileData())
.setMinApi(parameters.getApiLevel())
.addOptionsModification(options -> options.enableVarHandleDesugaring = true)
.run(parameters.getRuntime(), getMainClass())
.applyIf(
parameters.isDexRuntime()
&& parameters.asDexRuntime().getVersion().isOlderThanOrEqual(Version.V4_4_4),
- // TODO(sgjesse): Running on 4.0.4 and 4.4.4 needs to be checked. Output seems
- // correct,
- // but at the same time there are VFY errors on stderr.
+ // TODO(b/247076137): Running on 4.0.4 and 4.4.4 needs to be checked. Output seems
+ // correct, but at the same time there are VFY errors on stderr.
r -> r.assertFailureWithErrorThatThrows(NoSuchFieldException.class),
- r -> r.assertSuccessWithOutput(getExpectedOutput()));
+ r ->
+ r.assertSuccessWithOutput(
+ parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.T)
+ && parameters
+ .getDexRuntimeVersion()
+ .isNewerThanOrEqual(Version.V13_0_0)
+ ? getExpectedOutputForArtImplementation()
+ : getExpectedOutputForReferenceImplementation()))
+ .inspect(this::inspect);
} else {
testForD8(parameters.getBackend())
- .addProgramClassFileData(ZipUtils.readSingleEntry(VarHandle.jar(), getJarEntry()))
+ .addProgramClassFileData(getProgramClassFileData())
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), getMainClass())
.applyIf(
@@ -93,7 +213,7 @@
parameters.getApiLevel().isLessThan(AndroidApiLevel.P)
|| parameters.asDexRuntime().getVersion().isOlderThanOrEqual(Version.V8_1_0),
r -> r.assertFailure(),
- r -> r.assertSuccessWithOutput(getExpectedOutput()));
+ r -> r.assertSuccessWithOutput(getExpectedOutputForArtImplementation()));
}
}
@@ -101,10 +221,13 @@
@Test
public void testR8() throws Throwable {
testForR8(parameters.getBackend())
- // Use android.jar from Android T to get the VarHandle type.
- .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
- .addProgramClassFileData(ZipUtils.readSingleEntry(VarHandle.jar(), getJarEntry()))
- .addLibraryFiles()
+ .applyIf(
+ parameters.isDexRuntime(),
+ // Use android.jar from Android T to get the VarHandle type.
+ b -> b.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T)),
+ // Use system JDK to have references types including StringConcatFactory.
+ b -> b.addLibraryProvider(JdkClassFileProvider.fromSystemJdk()))
+ .addProgramClassFileData(getProgramClassFileData())
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(getMainClass())
.addKeepRules(getKeepRules())
@@ -121,6 +244,10 @@
&& (parameters.getApiLevel().isLessThan(AndroidApiLevel.P)
|| parameters.asDexRuntime().getVersion().isOlderThanOrEqual(Version.V8_1_0)),
r -> r.assertFailure(),
- r -> r.assertSuccessWithOutput(getExpectedOutput()));
+ r ->
+ r.assertSuccessWithOutput(
+ parameters.isCfRuntime()
+ ? getExpectedOutputForReferenceImplementation()
+ : getExpectedOutputForArtImplementation()));
}
}
diff --git a/src/test/java/com/android/tools/r8/debug/SmaliDebugTest.java b/src/test/java/com/android/tools/r8/debug/SmaliDebugTest.java
index 1f41abc..1d4a205 100644
--- a/src/test/java/com/android/tools/r8/debug/SmaliDebugTest.java
+++ b/src/test/java/com/android/tools/r8/debug/SmaliDebugTest.java
@@ -122,7 +122,7 @@
int target = cond.getTargets()[0] + cond.getOffset();
int linePC = -1;
for (DexDebugEntry entry : info.getEntries()) {
- if (entry.line == 4) {
+ if (entry.getPosition().getLine() == 4) {
linePC = entry.address;
break;
}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
index 605865f..00409e1 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
@@ -125,7 +125,7 @@
for (int i = 0; i < entries.size(); i++) {
DexDebugEntry entry = entries.get(i);
// Matches each entry at 'line' that is not a zero-line increment.
- if (entry.line == line && (i == 0 || entries.get(i - 1).line != line)) {
+ if (entry.getLine() == line && (i == 0 || entries.get(i - 1).getLine() != line)) {
found++;
check.accept(entry);
}
@@ -139,7 +139,11 @@
for (DebugLocalInfo local : entry.locals.values()) {
if (local.name.toString().equals(name)) {
if (found != null) {
- fail("Line " + entry.line + ". Local defined multiple times for name: " + name);
+ fail(
+ "Line "
+ + entry.getPosition().getLine()
+ + ". Local defined multiple times for name: "
+ + name);
}
assertEquals(type, local.type.toString());
if (typeParameters.length > 0) {
@@ -155,7 +159,7 @@
found = local;
}
}
- assertNotNull("Line " + entry.line + ". Failed to find local with name: " + name, found);
+ assertNotNull("Line " + entry.getLine() + ". Failed to find local with name: " + name, found);
return found;
}
@@ -176,9 +180,14 @@
remaining.remove(local);
}
}
- assertEquals("Line " + entry.line + ". Found unexpected locals: " +
- String.join(",", remaining.stream().map(Object::toString).collect(Collectors.toList())),
- expected, expected + remaining.size());
+ assertEquals(
+ "Line "
+ + entry.getLine()
+ + ". Found unexpected locals: "
+ + String.join(
+ ",", remaining.stream().map(Object::toString).collect(Collectors.toList())),
+ expected,
+ expected + remaining.size());
}
public DexEncodedMethod getMethod() {
diff --git a/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
index c6f0994..9d8cf281 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
@@ -146,7 +146,8 @@
MethodSubject main = clazz.uniqueMethodWithOriginalName("main");
List<DexDebugEntry> entries =
new DexDebugEntryBuilder(main.getMethod(), inspector.getFactory()).build();
- Set<Integer> lines = entries.stream().map(e -> e.line).collect(Collectors.toSet());
+ Set<Integer> lines =
+ entries.stream().map(e -> e.getPosition().getLine()).collect(Collectors.toSet());
assertFalse(lines.isEmpty());
}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java
index 6af4440..944b163 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java
@@ -97,7 +97,8 @@
assertEquals(TEST_CLASS, line.className);
assertEquals("main", line.methodName);
// Expected line number could be PC based or increments.
- // The test need not check what it is, just that all methods have been fully inlined.
+ // The test need not check what it is, just that all methods have been fully
+ // inlined.
assertEquals(2, rawStackTrace.size());
})
.inspectStackTrace(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesWatchEventTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesWatchEventTest.java
new file mode 100644
index 0000000..6f3b986
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesWatchEventTest.java
@@ -0,0 +1,124 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary.jdk11;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
+import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import java.util.List;
+import org.junit.Assume;
+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 FilesWatchEventTest extends DesugaredLibraryTestBase {
+
+ private static final String EXPECTED_RESULT =
+ StringUtils.lines("true", "true", "true", "ENTRY_CREATE", "foo", "true");
+ private static final String EXPECTED_RESULT_DESUGARING =
+ StringUtils.lines("class java.lang.UnsupportedOperationException :: null");
+
+ private final TestParameters parameters;
+ private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+ private final CompilationSpecification compilationSpecification;
+
+ @Parameters(name = "{0}, spec: {1}, {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ // Skip Android 4.4.4 due to missing libjavacrypto.
+ getTestParameters()
+ .withCfRuntime(CfVm.JDK11)
+ .withDexRuntime(Version.V4_0_4)
+ .withDexRuntimesStartingFromIncluding(Version.V5_1_1)
+ .withAllApiLevels()
+ .build(),
+ ImmutableList.of(JDK11_PATH),
+ DEFAULT_SPECIFICATIONS);
+ }
+
+ public FilesWatchEventTest(
+ TestParameters parameters,
+ LibraryDesugaringSpecification libraryDesugaringSpecification,
+ CompilationSpecification compilationSpecification) {
+ this.parameters = parameters;
+ this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+ this.compilationSpecification = compilationSpecification;
+ }
+
+ @Test
+ public void test() throws Throwable {
+ if (parameters.isCfRuntime() && !ToolHelper.isWindows()) {
+ // Reference runtime, we use Jdk 11 since this is Jdk 11 desugared library, not that Jdk 8
+ // behaves differently on this test.
+ Assume.assumeTrue(parameters.isCfRuntime(CfVm.JDK11) && !ToolHelper.isWindows());
+ testForJvm()
+ .addInnerClasses(getClass())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ return;
+ }
+ testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .compile()
+ .withArt6Plus64BitsLib()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(
+ libraryDesugaringSpecification.usesPlatformFileSystem(parameters)
+ ? EXPECTED_RESULT
+ : EXPECTED_RESULT_DESUGARING);
+ }
+
+ public static class TestClass {
+
+ public static void main(String[] args) throws IOException, InterruptedException {
+ Path dir = Files.createTempDirectory("tmpDictWatch");
+ FileSystem fs = FileSystems.getDefault();
+
+ try (WatchService watcher = fs.newWatchService()) {
+ WatchKey myKey = dir.register(watcher, ENTRY_CREATE);
+ System.out.println(myKey.isValid());
+ System.out.println(myKey.watchable().equals(dir));
+
+ Path file = dir.resolve("foo");
+ Files.createFile(file);
+
+ WatchKey key = watcher.take();
+ System.out.println(key.equals(myKey));
+
+ WatchEvent<?> event = myKey.pollEvents().iterator().next();
+ System.out.println(event.kind());
+ System.out.println(event.context());
+
+ System.out.println(myKey.reset());
+ Files.delete(file);
+ } catch (UnsupportedOperationException e) {
+ System.out.println(e.getClass() + " :: " + e.getMessage());
+ }
+
+ Files.delete(dir);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11NioFileTests.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11NioFileTests.java
index 8274628..9f3a07b 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11NioFileTests.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdktests/Jdk11NioFileTests.java
@@ -12,6 +12,7 @@
import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.D8_L8SHRINK;
import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
import static com.android.tools.r8.utils.FileUtils.JAVA_EXTENSION;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.D8TestCompileResult;
@@ -27,8 +28,10 @@
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.transformers.ClassFileTransformer;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -39,6 +42,7 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
import java.util.stream.Collectors;
import org.jetbrains.annotations.NotNull;
import org.junit.Assume;
@@ -74,7 +78,8 @@
// TODO(134732760): Support Dalvik VMs, currently fails because libjavacrypto is required
// and present only in ART runtimes.
getTestParameters()
- .withDexRuntimesStartingFromIncluding(Version.V10_0_0)
+ .withDexRuntime(Version.V4_0_4)
+ .withDexRuntimesStartingFromIncluding(Version.V5_1_1)
.withAllApiLevels()
.build(),
specs,
@@ -106,6 +111,16 @@
"module-info.java"
});
+ private static final Set<String> EXPECTED_FAILING_CLASSES_DESUGARING_26 =
+ ImmutableSet.of(
+ // SecureDirectoryStream is not supported with desugared library, which leads to issues.
+ "DirectoryStreamSecureDS",
+ // Watch services are supported on high apis with desugared library, however, only
+ // equality
+ // and not identity is preserved which fails the tests.
+ "WatchServiceBasic",
+ "WatchServiceSensitivityModifier");
+
// We distinguish 2 kinds of tests:
// - Main tests, which are run by running the main method, and succeed if no error is raised.
// - TestNG tests, which are run using testNG.
@@ -272,12 +287,12 @@
.compile()
.withArt6Plus64BitsLib();
int success = 0;
- int failures = 0;
+ List<String> failingClasses = new ArrayList<>();
for (String mainTestClass : SUCCESSFUL_MAIN_TESTS) {
SingleTestRunResult<?> run = compileResult.run(parameters.getRuntime(), mainTestClass);
if (run.getExitCode() != 0) {
System.out.println("Main Fail " + mainTestClass);
- failures++;
+ failingClasses.add(mainTestClass);
} else {
success++;
}
@@ -288,17 +303,31 @@
parameters.getRuntime(), "TestNGMainRunner", verbosity, testNGTestClass);
if (!result.getStdOut().contains(StringUtils.lines(testNGTestClass + ": SUCCESS"))) {
System.out.println("TestNG Fail " + testNGTestClass);
- failures++;
+ failingClasses.add(testNGTestClass);
} else {
success++;
}
}
- // TODO(b/234689867): Understand and fix these issues.
- // Most issues seem to come from the missing secure.properties file. This file is not accessed
- // in all tests on all API levels, hence a different number of failures on each level.
- System.out.println("Successes :" + success + "; failures " + failures);
- assertTrue(success >= 11);
- assertTrue(failures <= 20);
+ System.out.println("Successes:" + success + "; failures:" + failingClasses.size());
+ if (!failingClasses.isEmpty()) {
+ System.out.println("Failing classes: " + failingClasses);
+ }
+ if (parameters.getDexRuntimeVersion().isOlderThan(Version.V8_1_0)) {
+ // TODO(b/234689867): DesugaredFileSystemProvider is exercised, fix or understand remaining
+ // failures.
+ int sevenOffset = parameters.getDexRuntimeVersion() == Version.V7_0_0 ? -1 : 0;
+ int shrinkOffset = compilationSpecification.isL8Shrink() ? -1 : 0;
+ assertTrue(success >= 18 + sevenOffset + shrinkOffset);
+ } else if (parameters.getApiLevel().isLessThan(AndroidApiLevel.O)) {
+ // Desugaring high api level.
+ assertEquals(26, success);
+ assertEquals(3, failingClasses.size());
+ assertTrue(failingClasses.containsAll(EXPECTED_FAILING_CLASSES_DESUGARING_26));
+ } else {
+ // No desugaring or partial desugaring, high api level.
+ assertEquals(29, success);
+ assertEquals(0, failingClasses.size());
+ }
}
@Test
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 af2bb5b..30b3240 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
@@ -96,7 +96,7 @@
return new RuntimeException("java.lang.invoke.WrongMethodTypeException");
}
- int toIntIfPossible(Object value) {
+ int toIntIfPossible(Object value, boolean forReturnType) {
if (value instanceof Integer) {
return (Integer) value;
}
@@ -109,14 +109,18 @@
if (value instanceof Short) {
return (Short) value;
}
- throw desugarWrongMethodTypeException();
+ if (forReturnType) {
+ throw new ClassCastException();
+ } else {
+ throw desugarWrongMethodTypeException();
+ }
}
- long toLongIfPossible(Object value) {
+ long toLongIfPossible(Object value, boolean forReturnType) {
if (value instanceof Long) {
return (Long) value;
}
- return toIntIfPossible(value);
+ return toIntIfPossible(value, forReturnType);
}
// get variants.
@@ -131,19 +135,31 @@
}
int getInt(Object ct1) {
- return U.getInt(ct1, offset);
+ if (type == int.class) {
+ return U.getInt(ct1, offset);
+ } else if (type == long.class) {
+ return (int) U.getLong(ct1, offset);
+ } else {
+ return toIntIfPossible(U.getObject(ct1, offset), true);
+ }
}
long getLong(Object ct1) {
- return U.getLong(ct1, offset);
+ if (type == long.class) {
+ return U.getLong(ct1, offset);
+ } else if (type == int.class) {
+ return U.getInt(ct1, offset);
+ } else {
+ return toLongIfPossible(U.getObject(ct1, offset), true);
+ }
}
// set variants.
void set(Object ct1, Object newValue) {
if (type == int.class) {
- setInt(ct1, toIntIfPossible(newValue));
+ setInt(ct1, toIntIfPossible(newValue, false));
} else if (type == long.class) {
- setLong(ct1, toLongIfPossible(newValue));
+ setLong(ct1, toLongIfPossible(newValue, false));
} else {
U.putObject(ct1, offset, newValue);
}
@@ -162,37 +178,39 @@
void setLong(Object ct1, long newValue) {
if (type == long.class) {
U.putLong(ct1, offset, newValue);
- } else {
+ } else if (type == int.class) {
throw desugarWrongMethodTypeException();
+ } else {
+ U.putObject(ct1, offset, Long.valueOf(newValue));
}
}
- boolean compareAndSet(Object ct1, Object expetedValue, Object newValue) {
+ boolean compareAndSet(Object ct1, Object expectedValue, Object newValue) {
if (type == int.class) {
return U.compareAndSwapInt(
- ct1, offset, toIntIfPossible(expetedValue), toIntIfPossible((newValue)));
+ ct1, offset, toIntIfPossible(expectedValue, false), toIntIfPossible(newValue, false));
}
if (type == long.class) {
return U.compareAndSwapLong(
- ct1, offset, toLongIfPossible(expetedValue), toLongIfPossible((newValue)));
+ ct1, offset, toLongIfPossible(expectedValue, false), toLongIfPossible(newValue, false));
}
- return U.compareAndSwapObject(ct1, offset, expetedValue, newValue);
+ return U.compareAndSwapObject(ct1, offset, expectedValue, newValue);
}
- boolean compareAndSetInt(Object ct1, int expetedValue, int newValue) {
+ boolean compareAndSetInt(Object ct1, int expectedValue, int newValue) {
if (type == int.class) {
- return U.compareAndSwapInt(ct1, offset, expetedValue, newValue);
+ return U.compareAndSwapInt(ct1, offset, expectedValue, newValue);
} else if (type == long.class) {
- return U.compareAndSwapLong(ct1, offset, expetedValue, newValue);
+ return U.compareAndSwapLong(ct1, offset, expectedValue, newValue);
} else {
- return compareAndSet(ct1, expetedValue, newValue);
+ return compareAndSet(ct1, expectedValue, newValue);
}
}
- boolean compareAndSetLong(Object ct1, long expetedValue, long newValue) {
+ boolean compareAndSetLong(Object ct1, long expectedValue, long newValue) {
if (type == long.class) {
- return U.compareAndSwapLong(ct1, offset, expetedValue, newValue);
+ return U.compareAndSwapLong(ct1, offset, expectedValue, newValue);
}
- return compareAndSet(ct1, expetedValue, newValue);
+ return compareAndSet(ct1, expectedValue, newValue);
}
}
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 9bee408..f9d98e3 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationTest.java
@@ -3,6 +3,7 @@
// 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;
@@ -53,13 +54,14 @@
.addKeepRules(rules)
.addKeepMainRule(TestClass.class)
.setMinApi(parameters.getApiLevel())
+ .allowUnusedProguardConfigurationRules()
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED)
.inspect(this::checkOutput);
}
public List<Class<?>> getInputClasses() {
- return ImmutableList.of(TestClass.class, A.class, B.class);
+ return ImmutableList.of(TestClass.class, A.class, B.class, C.class);
}
public List<byte[]> getInputClassesWithoutAnnotations() throws Exception {
@@ -83,7 +85,9 @@
private void checkOutput(CodeInspector inspector) {
assertThat(inspector.clazz(A.class), isPresent());
assertThat(inspector.clazz(B.class), isPresent());
- assertThat(inspector.clazz(B.class).uniqueMethodWithOriginalName("bar"), isPresent());
+ assertThat(inspector.clazz(C.class), isAbsent());
+ assertThat(inspector.clazz(B.class).method("void", "bar"), isPresent());
+ assertThat(inspector.clazz(B.class).method("void", "bar", "int"), isAbsent());
}
static class A {
@@ -94,18 +98,36 @@
// Ensure that the class B remains as we are looking it up by reflected name.
@KeepTarget(classConstant = B.class),
// Ensure the method 'bar' remains as we are invoking it by reflected name.
- @KeepTarget(classConstant = B.class, methodName = "bar")
+ @KeepTarget(
+ classConstant = B.class,
+ methodName = "bar",
+ methodParameters = {},
+ methodReturnType = "void")
})
public void foo() throws Exception {
Class<?> clazz = Class.forName(A.class.getTypeName().replace("$A", "$B"));
clazz.getDeclaredMethod("bar").invoke(clazz);
}
+
+ // This annotation is not active as its implicit precondition "void A.foo(int)" is not used.
+ @UsesReflection({@KeepTarget(classConstant = C.class)})
+ public void foo(int unused) {
+ // Unused.
+ }
}
static class B {
public static void bar() {
System.out.println("Hello, world");
}
+
+ public static void bar(int ignore) {
+ throw new RuntimeException("UNUSED");
+ }
+ }
+
+ static class C {
+ // Unused.
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionFieldAnnotationTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionFieldAnnotationTest.java
new file mode 100644
index 0000000..b6cc560
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionFieldAnnotationTest.java
@@ -0,0 +1,131 @@
+// 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.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.CodeInspector;
+import com.google.common.collect.ImmutableList;
+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 KeepUsesReflectionFieldAnnotationTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("Hello, world");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build();
+ }
+
+ public KeepUsesReflectionFieldAnnotationTest(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();
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(getInputClassesWithoutAnnotations())
+ .addKeepRules(rules)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkOutput);
+ }
+
+ public List<Class<?>> getInputClasses() {
+ return ImmutableList.of(TestClass.class, A.class, B.class);
+ }
+
+ 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;
+ }
+
+ 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(A.class), isPresent());
+ assertThat(inspector.clazz(A.class).uniqueFieldWithOriginalName("classNameForB"), isPresent());
+ assertThat(inspector.clazz(B.class), isPresent());
+ assertThat(inspector.clazz(B.class).init(), isPresent());
+ assertThat(inspector.clazz(B.class).init("int"), isAbsent());
+ }
+
+ static class A {
+
+ @UsesReflection({
+ @KeepTarget(classConstant = B.class),
+ @KeepTarget(
+ classConstant = B.class,
+ methodName = "<init>",
+ methodParameters = {}),
+ })
+ public final String classNameForB =
+ System.nanoTime() == 0
+ ? null
+ : "com.android.tools.r8.keepanno.KeepUsesReflectionFieldAnnotationTest$B";
+
+ public B foo() throws Exception {
+ return (B) Class.forName(classNameForB).getDeclaredConstructor().newInstance();
+ }
+ }
+
+ static class B {
+ B() {
+ // Used.
+ }
+
+ B(int unused) {
+ // Unused.
+ }
+
+ public void bar() {
+ System.out.println("Hello, world");
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) throws Exception {
+ new A().foo().bar();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionOnFieldTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionOnFieldTest.java
new file mode 100644
index 0000000..6739144
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionOnFieldTest.java
@@ -0,0 +1,113 @@
+// 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.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.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class KeepUsesReflectionOnFieldTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("Hello, world");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build();
+ }
+
+ public KeepUsesReflectionOnFieldTest(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(getInputClassesWithoutAnnotations())
+ .addKeepRules(rules)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED)
+ .inspect(this::checkOutput);
+ }
+
+ public List<Class<?>> getInputClasses() {
+ return ImmutableList.of(TestClass.class, A.class);
+ }
+
+ 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;
+ }
+
+ 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(A.class), isPresent());
+ assertThat(inspector.clazz(A.class).uniqueFieldWithOriginalName("fieldA"), isPresent());
+ assertThat(inspector.clazz(A.class).uniqueFieldWithOriginalName("fieldB"), isAbsent());
+ }
+
+ static class A {
+
+ public String fieldA = "Hello, world";
+ public Integer fieldB = 42;
+
+ @UsesReflection({@KeepTarget(classConstant = A.class, fieldType = "java.lang.String")})
+ public void foo() throws Exception {
+ for (Field field : getClass().getDeclaredFields()) {
+ if (field.getType().equals(String.class)) {
+ System.out.println(field.get(this));
+ }
+ }
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) throws Exception {
+ new A().foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
index ff769a2..1b67cfc 100644
--- a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
+++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -10,8 +10,10 @@
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.synthesis.SyntheticNaming.Phase;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.google.common.collect.ImmutableList;
import java.lang.reflect.Method;
import org.hamcrest.Matcher;
@@ -51,6 +53,24 @@
originalMethod.getMethodDescriptor());
}
+ public static MethodReference syntheticBackportWithForwardingMethod(
+ ClassReference clazz, int id, MethodReference method) {
+ // For backports with forwarding the backported method is not static, so the original method
+ // signature has the receiver type pre-pended.
+ ImmutableList.Builder<TypeReference> builder = ImmutableList.builder();
+ builder.add(method.getHolderClass()).addAll(method.getFormalTypes());
+ MethodReference methodWithReceiverForForwarding =
+ Reference.method(
+ method.getHolderClass(),
+ method.getMethodName(),
+ builder.build(),
+ method.getReturnType());
+ return Reference.methodFromDescriptor(
+ syntheticBackportWithForwardingClass(clazz, id),
+ syntheticMethodName(),
+ methodWithReceiverForForwarding.getMethodDescriptor());
+ }
+
public static ClassReference syntheticOutlineClass(Class<?> clazz, int id) {
return syntheticClass(clazz, naming.OUTLINE, id);
}
@@ -85,6 +105,11 @@
return syntheticClass(classReference, naming.BACKPORT, id);
}
+ public static ClassReference syntheticBackportWithForwardingClass(
+ ClassReference classReference, int id) {
+ return syntheticClass(classReference, naming.BACKPORT_WITH_FORWARDING, id);
+ }
+
public static ClassReference syntheticTwrCloseResourceClass(Class<?> clazz, int id) {
return syntheticClass(clazz, naming.TWR_CLOSE_RESOURCE, id);
}
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 bcd5360..1974bcd 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -1494,7 +1494,7 @@
@Override
public FieldVisitor visitField(
int access, String name, String descriptor, String signature, Object value) {
- FieldVisitor fv = visitField(access, name, descriptor, signature, value);
+ FieldVisitor fv = super.visitField(access, name, descriptor, signature, value);
return new FieldVisitor(ASM_VERSION, fv) {
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
diff --git a/tools/r8_release.py b/tools/r8_release.py
index fe921e2..78fd031 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -193,8 +193,7 @@
# Don't upload if requested not to, or if changes are not committed due
# to --use-existing-work-branch
if not options.no_upload and not options.use_existing_work_branch:
- # TODO(b/260680525): Consider removing "-o banned-words~skip" if b/260680525 is resolved.
- process = subprocess.Popen(['repo', 'upload', '.', '--verify', '-o' 'banned-words~skip'],
+ process = subprocess.Popen(['repo', 'upload', '.', '--verify'],
stdin=subprocess.PIPE)
return process.communicate(input=b'y\n')[0]