Merge commit '7a298ac1ef617d3a2d2d985057b971c7240e4519' into dev-release
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 9d0265e..6d66d0a 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
@@ -8,6 +8,28 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+/**
+ * A condition for a keep edge.
+ *
+ * <p>The condition denotes a keep item:
+ *
+ * <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 condition item is the same as for a target item but without a notion of
+ * "keep options".
+ */
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.CLASS)
-public @interface KeepCondition {}
+public @interface KeepCondition {
+ Class<?> classConstant() default Object.class;
+
+ String classTypeName() default "";
+
+ String methodName() default "";
+
+ String fieldName() 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 5b6de6e..ca219b2 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
@@ -26,11 +26,20 @@
public static final String consequences = "consequences";
}
- public static final class Target {
- public static final Class<KeepTarget> CLASS = KeepTarget.class;
- public static final String DESCRIPTOR = getDescriptor(CLASS);
+ // 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 fieldName = "fieldName";
}
+
+ public static final class Condition {
+ public static final Class<KeepCondition> CLASS = KeepCondition.class;
+ public static final String DESCRIPTOR = getDescriptor(CLASS);
+ }
+
+ public static final class Target {
+ public static final Class<KeepTarget> CLASS = KeepTarget.class;
+ public static final String DESCRIPTOR = getDescriptor(CLASS);
+ }
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepEdge.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepEdge.java
index 19b1a4a..fb9a772 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepEdge.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepEdge.java
@@ -8,7 +8,7 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-@Target(ElementType.TYPE)
+@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface KeepEdge {
KeepCondition[] preconditions() 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 cc6e4ee..dd5be82 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeReader.java
@@ -3,8 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.keepanno.asm;
+import com.android.tools.r8.keepanno.annotations.KeepConstants.Condition;
import com.android.tools.r8.keepanno.annotations.KeepConstants.Edge;
+import com.android.tools.r8.keepanno.annotations.KeepConstants.Item;
import com.android.tools.r8.keepanno.annotations.KeepConstants.Target;
+import com.android.tools.r8.keepanno.ast.KeepCondition;
import com.android.tools.r8.keepanno.ast.KeepConsequences;
import com.android.tools.r8.keepanno.ast.KeepEdge;
import com.android.tools.r8.keepanno.ast.KeepEdgeException;
@@ -22,6 +25,8 @@
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
@@ -52,6 +57,54 @@
}
return null;
}
+
+ @Override
+ public MethodVisitor visitMethod(
+ int access, String name, String descriptor, String signature, String[] exceptions) {
+ return new KeepEdgeMethodVisitor(parent);
+ }
+
+ @Override
+ public FieldVisitor visitField(
+ int access, String name, String descriptor, String signature, Object value) {
+ return new KeepEdgeFieldVisitor(parent);
+ }
+ }
+
+ private static class KeepEdgeMethodVisitor extends MethodVisitor {
+ private final Parent<KeepEdge> parent;
+
+ KeepEdgeMethodVisitor(Parent<KeepEdge> parent) {
+ super(ASM_VERSION);
+ this.parent = parent;
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+ // Skip any visible annotations as @KeepEdge is not runtime visible.
+ if (!visible && descriptor.equals(Edge.DESCRIPTOR)) {
+ return new KeepEdgeVisitor(parent);
+ }
+ return null;
+ }
+ }
+
+ private static class KeepEdgeFieldVisitor extends FieldVisitor {
+ private final Parent<KeepEdge> parent;
+
+ KeepEdgeFieldVisitor(Parent<KeepEdge> parent) {
+ super(ASM_VERSION);
+ this.parent = parent;
+ }
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+ // Skip any visible annotations as @KeepEdge is not runtime visible.
+ if (!visible && descriptor.equals(Edge.DESCRIPTOR)) {
+ return new KeepEdgeVisitor(parent);
+ }
+ return null;
+ }
}
// Interface for providing AST result(s) for a sub-tree back up to its parent.
@@ -113,10 +166,24 @@
private static class KeepPreconditionsVisitor extends AnnotationVisitorBase {
private final Parent<KeepPreconditions> parent;
+ private final KeepPreconditions.Builder builder = KeepPreconditions.builder();
public KeepPreconditionsVisitor(Parent<KeepPreconditions> parent) {
this.parent = parent;
}
+
+ @Override
+ public AnnotationVisitor visitAnnotation(String name, String descriptor) {
+ if (descriptor.equals(Condition.DESCRIPTOR)) {
+ return new KeepConditionVisitor(builder::addCondition);
+ }
+ return super.visitAnnotation(name, descriptor);
+ }
+
+ @Override
+ public void visitEnd() {
+ parent.accept(builder.build());
+ }
}
private static class KeepConsequencesVisitor extends AnnotationVisitorBase {
@@ -141,27 +208,28 @@
}
}
- private static class KeepTargetVisitor extends AnnotationVisitorBase {
- private final Parent<KeepTarget> parent;
+ private abstract static class KeepItemVisitorBase extends AnnotationVisitorBase {
+ private final Parent<KeepItemPattern> parent;
+
private KeepQualifiedClassNamePattern classNamePattern = null;
private KeepMethodNamePattern methodName = null;
private KeepFieldNamePattern fieldName = null;
- public KeepTargetVisitor(Parent<KeepTarget> parent) {
+ public KeepItemVisitorBase(Parent<KeepItemPattern> parent) {
this.parent = parent;
}
@Override
public void visit(String name, Object value) {
- if (name.equals(Target.classConstant) && value instanceof Type) {
+ if (name.equals(Item.classConstant) && value instanceof Type) {
classNamePattern = KeepQualifiedClassNamePattern.exact(((Type) value).getClassName());
return;
}
- if (name.equals(Target.methodName) && value instanceof String) {
+ if (name.equals(Item.methodName) && value instanceof String) {
methodName = KeepMethodNamePattern.exact((String) value);
return;
}
- if (name.equals(Target.fieldName) && value instanceof String) {
+ if (name.equals(Item.fieldName) && value instanceof String) {
fieldName = KeepFieldNamePattern.exact((String) value);
return;
}
@@ -184,8 +252,21 @@
if (fieldName != null) {
itemBuilder.setMemberPattern(KeepFieldPattern.builder().setNamePattern(fieldName).build());
}
- KeepTarget target = KeepTarget.builder().setItem(itemBuilder.build()).build();
- parent.accept(target);
+ parent.accept(itemBuilder.build());
+ }
+ }
+
+ private static class KeepTargetVisitor extends KeepItemVisitorBase {
+
+ public KeepTargetVisitor(Parent<KeepTarget> parent) {
+ super(item -> parent.accept(KeepTarget.builder().setItem(item).build()));
+ }
+ }
+
+ private static class KeepConditionVisitor extends KeepItemVisitorBase {
+
+ public KeepConditionVisitor(Parent<KeepCondition> parent) {
+ super(item -> parent.accept(KeepCondition.builder().setItem(item).build()));
}
}
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
index 0a0ccc8..a8f631a 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
@@ -3,7 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.keepanno.asm;
+import com.android.tools.r8.keepanno.annotations.KeepConstants;
+import com.android.tools.r8.keepanno.annotations.KeepConstants.Condition;
import com.android.tools.r8.keepanno.annotations.KeepConstants.Edge;
+import com.android.tools.r8.keepanno.annotations.KeepConstants.Item;
import com.android.tools.r8.keepanno.annotations.KeepConstants.Target;
import com.android.tools.r8.keepanno.ast.KeepConsequences;
import com.android.tools.r8.keepanno.ast.KeepEdge;
@@ -44,7 +47,15 @@
if (preconditions.isAlways()) {
return;
}
- throw new Unimplemented();
+ String ignoredArrayValueName = null;
+ AnnotationVisitor arrayVisitor = visitor.visitArray(Edge.preconditions);
+ preconditions.forEach(
+ condition -> {
+ AnnotationVisitor conditionVisitor =
+ arrayVisitor.visitAnnotation(ignoredArrayValueName, Condition.DESCRIPTOR);
+ writeItem(conditionVisitor, condition.getItemPattern());
+ });
+ arrayVisitor.visitEnd();
}
private void writeConsequences(AnnotationVisitor visitor, KeepConsequences consequences) {
@@ -59,26 +70,29 @@
if (!target.getOptions().isKeepAll()) {
throw new Unimplemented();
}
- KeepItemPattern item = target.getItem();
- if (item.isAny()) {
- throw new Unimplemented();
- }
- KeepQualifiedClassNamePattern namePattern = item.getClassNamePattern();
- if (namePattern.isExact()) {
- Type typeConstant = Type.getType(namePattern.getExactDescriptor());
- targetVisitor.visit(Target.classConstant, typeConstant);
- } else {
- throw new Unimplemented();
- }
- if (!item.getExtendsPattern().isAny()) {
- throw new Unimplemented();
- }
- writeMember(item.getMemberPattern(), targetVisitor);
- targetVisitor.visitEnd();
+ writeItem(targetVisitor, target.getItem());
});
arrayVisitor.visitEnd();
}
+ private void writeItem(AnnotationVisitor itemVisitor, KeepItemPattern item) {
+ if (item.isAny()) {
+ throw new Unimplemented();
+ }
+ KeepQualifiedClassNamePattern namePattern = item.getClassNamePattern();
+ if (namePattern.isExact()) {
+ Type typeConstant = Type.getType(namePattern.getExactDescriptor());
+ itemVisitor.visit(KeepConstants.Item.classConstant, typeConstant);
+ } else {
+ throw new Unimplemented();
+ }
+ if (!item.getExtendsPattern().isAny()) {
+ throw new Unimplemented();
+ }
+ writeMember(item.getMemberPattern(), itemVisitor);
+ itemVisitor.visitEnd();
+ }
+
private void writeMember(KeepMemberPattern memberPattern, AnnotationVisitor targetVisitor) {
if (memberPattern.isNone()) {
// Default is "no methods".
@@ -99,7 +113,7 @@
private void writeField(KeepFieldPattern field, AnnotationVisitor targetVisitor) {
KeepFieldNameExactPattern exactFieldName = field.getNamePattern().asExact();
if (exactFieldName != null) {
- targetVisitor.visit(Target.fieldName, exactFieldName.getName());
+ targetVisitor.visit(Item.fieldName, exactFieldName.getName());
} else {
throw new Unimplemented();
}
@@ -114,7 +128,7 @@
private void writeMethod(KeepMethodPattern method, AnnotationVisitor targetVisitor) {
KeepMethodNameExactPattern exactMethodName = method.getNamePattern().asExact();
if (exactMethodName != null) {
- targetVisitor.visit(Target.methodName, exactMethodName.getName());
+ targetVisitor.visit(Item.methodName, exactMethodName.getName());
} else {
throw new Unimplemented();
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCondition.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCondition.java
index 43b4176..9909097 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCondition.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepCondition.java
@@ -36,10 +36,33 @@
private final KeepItemPattern itemPattern;
private KeepCondition(KeepItemPattern itemPattern) {
+ assert itemPattern != null;
this.itemPattern = itemPattern;
}
public KeepItemPattern getItemPattern() {
return itemPattern;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ KeepCondition that = (KeepCondition) o;
+ return itemPattern.equals(that.itemPattern);
+ }
+
+ @Override
+ public int hashCode() {
+ return itemPattern.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return itemPattern.toString();
+ }
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessor.java b/src/keepanno/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessor.java
index 18d3932..fe49466 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessor.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/processor/KeepEdgeProcessor.java
@@ -9,9 +9,10 @@
import com.android.tools.r8.keepanno.annotations.KeepConstants;
import com.android.tools.r8.keepanno.annotations.KeepConstants.Edge;
-import com.android.tools.r8.keepanno.annotations.KeepConstants.Target;
+import com.android.tools.r8.keepanno.annotations.KeepConstants.Item;
import com.android.tools.r8.keepanno.asm.KeepEdgeReader;
import com.android.tools.r8.keepanno.asm.KeepEdgeWriter;
+import com.android.tools.r8.keepanno.ast.KeepCondition;
import com.android.tools.r8.keepanno.ast.KeepConsequences;
import com.android.tools.r8.keepanno.ast.KeepEdge;
import com.android.tools.r8.keepanno.ast.KeepEdge.Builder;
@@ -21,12 +22,15 @@
import com.android.tools.r8.keepanno.ast.KeepItemPattern;
import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern;
import com.android.tools.r8.keepanno.ast.KeepMethodPattern;
+import com.android.tools.r8.keepanno.ast.KeepPreconditions;
import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern;
import com.android.tools.r8.keepanno.ast.KeepTarget;
-import com.android.tools.r8.keepanno.utils.Unimplemented;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Consumer;
import javax.annotation.processing.AbstractProcessor;
@@ -58,26 +62,33 @@
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
- for (Element rootElement : roundEnv.getRootElements()) {
- TypeElement typeElement = getEnclosingTypeElement(rootElement);
- KeepEdge edge = processKeepEdge(typeElement, roundEnv);
- if (edge != null) {
- String edgeTargetClass =
- getClassTypeNameForSynthesizedEdges(typeElement.getQualifiedName().toString());
- byte[] writtenEdge = writeEdge(edge, edgeTargetClass);
- Filer filer = processingEnv.getFiler();
- try {
- JavaFileObject classFile = filer.createClassFile(edgeTargetClass);
- classFile.openOutputStream().write(writtenEdge);
- } catch (IOException e) {
- error(e.getMessage());
+ Map<String, List<KeepEdge>> collectedEdges = new HashMap<>();
+ for (TypeElement annotation : annotations) {
+ for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) {
+ KeepEdge edge = processKeepEdge(element, roundEnv);
+ if (edge != null) {
+ TypeElement enclosingType = getEnclosingTypeElement(element);
+ String enclosingTypeName = enclosingType.getQualifiedName().toString();
+ collectedEdges.computeIfAbsent(enclosingTypeName, k -> new ArrayList<>()).add(edge);
}
}
}
+ for (Entry<String, List<KeepEdge>> entry : collectedEdges.entrySet()) {
+ String enclosingTypeName = entry.getKey();
+ String edgeTargetClass = getClassTypeNameForSynthesizedEdges(enclosingTypeName);
+ byte[] writtenEdge = writeEdges(entry.getValue(), edgeTargetClass);
+ Filer filer = processingEnv.getFiler();
+ try {
+ JavaFileObject classFile = filer.createClassFile(edgeTargetClass);
+ classFile.openOutputStream().write(writtenEdge);
+ } catch (IOException e) {
+ error(e.getMessage());
+ }
+ }
return true;
}
- private static byte[] writeEdge(KeepEdge edge, String classTypeName) {
+ private static byte[] writeEdges(List<KeepEdge> edges, String classTypeName) {
String classBinaryName = KeepConstants.getBinaryNameFromClassTypeName(classTypeName);
ClassWriter classWriter = new ClassWriter(0);
classWriter.visit(
@@ -88,13 +99,15 @@
"java/lang/Object",
null);
classWriter.visitSource("SynthesizedKeepEdge", null);
- KeepEdgeWriter.writeEdge(edge, classWriter);
+ for (KeepEdge edge : edges) {
+ KeepEdgeWriter.writeEdge(edge, classWriter);
+ }
classWriter.visitEnd();
return classWriter.toByteArray();
}
- private KeepEdge processKeepEdge(TypeElement keepEdge, RoundEnvironment roundEnv) {
- AnnotationMirror mirror = getAnnotationMirror(keepEdge, KeepConstants.Edge.CLASS);
+ private KeepEdge processKeepEdge(Element element, RoundEnvironment roundEnv) {
+ AnnotationMirror mirror = getAnnotationMirror(element, KeepConstants.Edge.CLASS);
if (mirror == null) {
return null;
}
@@ -109,7 +122,15 @@
if (preconditions == null) {
return;
}
- throw new Unimplemented();
+ KeepPreconditions.Builder preconditionsBuilder = KeepPreconditions.builder();
+ new AnnotationListValueVisitor(
+ value -> {
+ KeepCondition.Builder conditionBuilder = KeepCondition.builder();
+ processCondition(conditionBuilder, AnnotationMirrorValueVisitor.getMirror(value));
+ preconditionsBuilder.addCondition(conditionBuilder.build());
+ })
+ .onValue(preconditions);
+ edgeBuilder.setPreconditions(preconditionsBuilder.build());
}
private void processConsequences(Builder edgeBuilder, AnnotationMirror mirror) {
@@ -141,31 +162,41 @@
}
}
+ private void processCondition(KeepCondition.Builder builder, AnnotationMirror mirror) {
+ KeepItemPattern.Builder itemBuilder = KeepItemPattern.builder();
+ processItem(itemBuilder, mirror);
+ builder.setItem(itemBuilder.build());
+ }
+
private void processTarget(KeepTarget.Builder builder, AnnotationMirror mirror) {
KeepItemPattern.Builder itemBuilder = KeepItemPattern.builder();
- AnnotationValue classConstantValue = getAnnotationValue(mirror, Target.classConstant);
+ processItem(itemBuilder, mirror);
+ builder.setItem(itemBuilder.build());
+ }
+
+ private void processItem(KeepItemPattern.Builder builder, AnnotationMirror mirror) {
+ AnnotationValue classConstantValue = getAnnotationValue(mirror, Item.classConstant);
if (classConstantValue != null) {
DeclaredType type = AnnotationClassValueVisitor.getType(classConstantValue);
String typeName = getTypeNameForClassConstantElement(type);
- itemBuilder.setClassPattern(KeepQualifiedClassNamePattern.exact(typeName));
+ builder.setClassPattern(KeepQualifiedClassNamePattern.exact(typeName));
}
- AnnotationValue methodNameValue = getAnnotationValue(mirror, Target.methodName);
- AnnotationValue fieldNameValue = getAnnotationValue(mirror, Target.fieldName);
+ AnnotationValue methodNameValue = getAnnotationValue(mirror, Item.methodName);
+ AnnotationValue fieldNameValue = getAnnotationValue(mirror, Item.fieldName);
if (methodNameValue != null && fieldNameValue != null) {
throw new KeepEdgeException("Cannot define both a method and a field name pattern");
}
if (methodNameValue != null) {
String methodName = AnnotationStringValueVisitor.getString(methodNameValue);
- itemBuilder.setMemberPattern(
+ builder.setMemberPattern(
KeepMethodPattern.builder()
.setNamePattern(KeepMethodNamePattern.exact(methodName))
.build());
} else if (fieldNameValue != null) {
String fieldName = AnnotationStringValueVisitor.getString(fieldNameValue);
- itemBuilder.setMemberPattern(
+ builder.setMemberPattern(
KeepFieldPattern.builder().setNamePattern(KeepFieldNamePattern.exact(fieldName)).build());
}
- builder.setItem(itemBuilder.build());
}
private void error(String message) {
@@ -181,9 +212,9 @@
}
}
- private static AnnotationMirror getAnnotationMirror(TypeElement typeElement, Class<?> clazz) {
+ private static AnnotationMirror getAnnotationMirror(Element element, Class<?> clazz) {
String clazzName = clazz.getName();
- for (AnnotationMirror m : typeElement.getAnnotationMirrors()) {
+ for (AnnotationMirror m : element.getAnnotationMirrors()) {
if (m.getAnnotationType().toString().equals(clazzName)) {
return m;
}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 71898e2..5a791ab 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -51,6 +51,7 @@
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
import com.android.tools.r8.ir.desugar.records.RecordDesugaring;
import com.android.tools.r8.ir.desugar.records.RecordFieldValuesRewriter;
+import com.android.tools.r8.ir.desugar.varhandle.VarHandleDesugaring;
import com.android.tools.r8.ir.optimize.AssertionsRewriter;
import com.android.tools.r8.ir.optimize.Inliner;
import com.android.tools.r8.ir.optimize.NestReducer;
@@ -300,6 +301,9 @@
if (options.shouldDesugarRecords()) {
RecordDesugaring.registerSynthesizedCodeReferences(appView.dexItemFactory());
}
+ if (options.shouldDesugarVarHandle()) {
+ VarHandleDesugaring.registerSynthesizedCodeReferences(appView.dexItemFactory());
+ }
CfUtilityMethodsForCodeOptimizations.registerSynthesizedCodeReferences(
appView.dexItemFactory());
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index 10acb93..23b16cc 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -320,7 +320,9 @@
return new DexApplicationReadFlags(
hasReadProgramResourceFromDex,
hasReadProgramResourceFromCf,
- application.getRecordWitnesses());
+ application.getRecordWitnesses(),
+ application.getVarHandleWitnesses(),
+ application.getMethodHandlesLookupWitnesses());
}
private void readDexSources(List<ProgramResource> dexSources, Queue<DexProgramClass> classes)
diff --git a/src/main/java/com/android/tools/r8/graph/ApplicationReaderMap.java b/src/main/java/com/android/tools/r8/graph/ApplicationReaderMap.java
index 496066b3..cec1de3 100644
--- a/src/main/java/com/android/tools/r8/graph/ApplicationReaderMap.java
+++ b/src/main/java/com/android/tools/r8/graph/ApplicationReaderMap.java
@@ -5,6 +5,8 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
public abstract class ApplicationReaderMap {
@@ -17,10 +19,14 @@
public abstract DexType getInvertedType(DexType type);
public static ApplicationReaderMap getInstance(InternalOptions options) {
+ ApplicationReaderMap result = new EmptyMap();
if (options.shouldDesugarRecords() && !options.testing.disableRecordApplicationReaderMap) {
- return new RecordMap(options.dexItemFactory());
+ result = new RecordMap(options.dexItemFactory());
}
- return new EmptyMap();
+ if (options.shouldDesugarVarHandle()) {
+ return new VarHandleMap(result);
+ }
+ return result;
}
public static class EmptyMap extends ApplicationReaderMap {
@@ -66,4 +72,34 @@
return type == factory.recordType ? factory.recordTagType : type;
}
}
+
+ public static class VarHandleMap extends ApplicationReaderMap {
+
+ private final ApplicationReaderMap previous;
+ private final Map<String, String> descriptorMap =
+ ImmutableMap.of(
+ DexItemFactory.varHandleDescriptorString,
+ DexItemFactory.desugarVarHandleDescriptorString,
+ DexItemFactory.methodHandlesLookupDescriptorString,
+ DexItemFactory.desugarMethodHandlesLookupDescriptorString);
+
+ public VarHandleMap(ApplicationReaderMap previous) {
+ this.previous = previous;
+ }
+
+ @Override
+ public String getDescriptor(String descriptor) {
+ return previous.getDescriptor(descriptorMap.getOrDefault(descriptor, descriptor));
+ }
+
+ @Override
+ public DexType getType(DexType type) {
+ return type;
+ }
+
+ @Override
+ public DexType getInvertedType(DexType type) {
+ return type;
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplicationReadFlags.java b/src/main/java/com/android/tools/r8/graph/DexApplicationReadFlags.java
index 7bce5f6..b64ef99 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplicationReadFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplicationReadFlags.java
@@ -12,14 +12,20 @@
private final boolean hasReadProgramClassFromDex;
private final boolean hasReadProgramClassFromCf;
private final Set<DexType> recordWitnesses;
+ private final Set<DexType> varHandleWitnesses;
+ private final Set<DexType> methodHandlesLookupWitnesses;
public DexApplicationReadFlags(
boolean hasReadProgramClassFromDex,
boolean hasReadProgramClassFromCf,
- Set<DexType> recordWitnesses) {
+ Set<DexType> recordWitnesses,
+ Set<DexType> varHandleWitnesses,
+ Set<DexType> methodHandlesLookupWitnesses) {
this.hasReadProgramClassFromDex = hasReadProgramClassFromDex;
this.hasReadProgramClassFromCf = hasReadProgramClassFromCf;
this.recordWitnesses = recordWitnesses;
+ this.varHandleWitnesses = varHandleWitnesses;
+ this.methodHandlesLookupWitnesses = methodHandlesLookupWitnesses;
}
public boolean hasReadProgramClassFromCf() {
@@ -37,4 +43,12 @@
public Set<DexType> getRecordWitnesses() {
return recordWitnesses;
}
+
+ public boolean hasReadVarHandleReferenceFromProgramClass() {
+ return !varHandleWitnesses.isEmpty();
+ }
+
+ public Set<DexType> getVarHandleWitnesses() {
+ return varHandleWitnesses;
+ }
}
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 c1e856e..888bbfe 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -78,6 +78,10 @@
public static final String desugarVarHandleDescriptorString =
"Lcom/android/tools/r8/DesugarVarHandle;";
public static final String varHandleDescriptorString = "Ljava/lang/invoke/VarHandle;";
+ public static final String desugarMethodHandlesLookupDescriptorString =
+ "Lcom/android/tools/r8/DesugarMethodHandlesLookup;";
+ public static final String methodHandlesLookupDescriptorString =
+ "Ljava/lang/invoke/MethodHandles$Lookup;";
public static final String dalvikAnnotationOptimizationPrefixString =
"Ldalvik/annotation/optimization/";
@@ -271,6 +275,9 @@
public final DexString varHandleDescriptor = createString(varHandleDescriptorString);
public final DexString methodHandleDescriptor = createString("Ljava/lang/invoke/MethodHandle;");
+ public final DexString methodHandlesDescriptor = createString("Ljava/lang/invoke/MethodHandles;");
+ public final DexString methodHandlesLookupDescriptor =
+ createString("Ljava/lang/invoke/MethodHandles$Lookup;");
public final DexString methodTypeDescriptor = createString("Ljava/lang/invoke/MethodType;");
public final DexString invocationHandlerDescriptor =
createString("Ljava/lang/reflect/InvocationHandler;");
@@ -353,6 +360,11 @@
public final DexString dalvikAnnotationOptimizationPrefix =
createString(dalvikAnnotationOptimizationPrefixString);
+ // Method names used on VarHandle.
+ public final DexString getString = createString("get");
+ public final DexString setString = createString("set");
+ public final DexString compareAndSetString = createString("compareAndSet");
+
public final DexType booleanType = createStaticallyKnownType(booleanDescriptor);
public final DexType byteType = createStaticallyKnownType(byteDescriptor);
public final DexType charType = createStaticallyKnownType(charDescriptor);
@@ -426,6 +438,9 @@
public final DexType varHandleType = createStaticallyKnownType(varHandleDescriptor);
public final DexType methodHandleType = createStaticallyKnownType(methodHandleDescriptor);
+ public final DexType methodHandlesType = createStaticallyKnownType(methodHandlesDescriptor);
+ public final DexType methodHandlesLookupType =
+ createStaticallyKnownType(methodHandlesLookupDescriptor);
public final DexType methodTypeType = createStaticallyKnownType(methodTypeDescriptor);
public final DexType invocationHandlerType =
createStaticallyKnownType(invocationHandlerDescriptor);
@@ -737,6 +752,8 @@
public final DexType unsafeType = createStaticallyKnownType("Lsun/misc/Unsafe;");
public final DexType desugarVarHandleType =
createStaticallyKnownType(desugarVarHandleDescriptorString);
+ public final DexType desugarMethodHandlesLookupType =
+ createStaticallyKnownType(desugarMethodHandlesLookupDescriptorString);
public final ObjectMethodsMembers objectMethodsMembers = new ObjectMethodsMembers();
public final ServiceLoaderMethods serviceLoaderMethods = new ServiceLoaderMethods();
@@ -1092,7 +1109,8 @@
public final DexMethod put =
createMethod(androidUtilSparseArrayType, createProto(voidType, intType, objectType), "put");
public final DexMethod set =
- createMethod(androidUtilSparseArrayType, createProto(voidType, intType, objectType), "set");
+ createMethod(
+ androidUtilSparseArrayType, createProto(voidType, intType, objectType), setString);
}
public class BooleanMembers extends BoxedPrimitiveMembers {
@@ -2232,7 +2250,7 @@
public class SupplierMembers extends LibraryMembers {
- public final DexMethod get = createMethod(supplierType, createProto(objectType), "get");
+ public final DexMethod get = createMethod(supplierType, createProto(objectType), getString);
private SupplierMembers() {}
}
@@ -2248,7 +2266,7 @@
"compareAndExchange",
"compareAndExchangeAcquire",
"compareAndExchangeRelease",
- "get",
+ getString,
"getAcquire",
"getAndAdd",
"getAndAddAcquire",
@@ -2269,11 +2287,11 @@
"getVolatile");
private final Set<DexString> varHandleSetMethods =
- createStrings("set", "setOpaque", "setRelease", "setVolatile");
+ createStrings(setString, "setOpaque", "setRelease", "setVolatile");
private final Set<DexString> varHandleCompareAndSetMethods =
createStrings(
- "compareAndSet",
+ compareAndSetString,
"weakCompareAndSet",
"weakCompareAndSetAcquire",
"weakCompareAndSetPlain",
@@ -2298,10 +2316,11 @@
return result;
}
- private Set<DexString> createStrings(String... strings) {
+ private Set<DexString> createStrings(Object... strings) {
IdentityHashMap<DexString, DexString> map = new IdentityHashMap<>();
- for (String string : strings) {
- DexString dexString = createString(string);
+ for (Object string : strings) {
+ DexString dexString =
+ string instanceof String ? createString((String) string) : (DexString) string;
map.put(dexString, dexString);
}
return map.keySet();
diff --git a/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java b/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java
index d342780..93e56a7 100644
--- a/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarApplicationReader.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
import com.android.tools.r8.ir.desugar.records.RecordDesugaring;
+import com.android.tools.r8.ir.desugar.varhandle.VarHandleDesugaring;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.Sets;
@@ -28,6 +29,8 @@
private final ConcurrentHashMap<String, DexString> stringCache = new ConcurrentHashMap<>();
private final ApplicationReaderMap applicationReaderMap;
private final Set<DexType> recordWitnesses = Sets.newConcurrentHashSet();
+ private final Set<DexType> varHandleWitnesses = Sets.newConcurrentHashSet();
+ private final Set<DexType> methodHandlesLookupWitnesses = Sets.newConcurrentHashSet();
private boolean hasReadRecordReferenceFromProgramClass = false;
@@ -177,4 +180,52 @@
addRecordWitness(dexMethod.getHolderType(), classKind);
}
}
+
+ public void addVarHandleWitness(DexType witness, ClassKind<?> classKind) {
+ if (classKind == ClassKind.PROGRAM) {
+ varHandleWitnesses.add(witness);
+ }
+ }
+
+ public Set<DexType> getVarHandleWitnesses() {
+ return varHandleWitnesses;
+ }
+
+ public void checkFieldForVarHandle(DexField dexField, ClassKind<?> classKind) {
+ if (options.shouldDesugarVarHandle()
+ && VarHandleDesugaring.refersToVarHandle(dexField, getFactory())) {
+ addVarHandleWitness(dexField.getHolderType(), classKind);
+ }
+ }
+
+ public void checkMethodForVarHandle(DexMethod dexMethod, ClassKind<?> classKind) {
+ if (options.shouldDesugarVarHandle()
+ && VarHandleDesugaring.refersToVarHandle(dexMethod, getFactory())) {
+ addVarHandleWitness(dexMethod.getHolderType(), classKind);
+ }
+ }
+
+ public void addMethodHandlesLookupWitness(DexType witness, ClassKind<?> classKind) {
+ if (classKind == ClassKind.PROGRAM) {
+ methodHandlesLookupWitnesses.add(witness);
+ }
+ }
+
+ public Set<DexType> getMethodHandlesLookupWitnesses() {
+ return methodHandlesLookupWitnesses;
+ }
+
+ public void checkFieldForMethodHandlesLookup(DexField dexField, ClassKind<?> classKind) {
+ if (options.shouldDesugarVarHandle()
+ && VarHandleDesugaring.refersToMethodHandlesLookup(dexField, getFactory())) {
+ addMethodHandlesLookupWitness(dexField.getHolderType(), classKind);
+ }
+ }
+
+ public void checkMethodForMethodHandlesLookup(DexMethod dexMethod, ClassKind<?> classKind) {
+ if (options.shouldDesugarVarHandle()
+ && VarHandleDesugaring.refersToMethodHandlesLookup(dexMethod, getFactory())) {
+ addMethodHandlesLookupWitness(dexMethod.getHolderType(), classKind);
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 6b9015b..fe8d9e4 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -688,6 +688,8 @@
FieldAccessFlags flags = createFieldAccessFlags(access);
DexField dexField = parent.application.getField(parent.type, name, desc);
parent.application.checkFieldForRecord(dexField, parent.classKind);
+ parent.application.checkFieldForMethodHandlesLookup(dexField, parent.classKind);
+ parent.application.checkFieldForVarHandle(dexField, parent.classKind);
Wrapper<DexField> signature = FieldSignatureEquivalence.get().wrap(dexField);
if (parent.fieldSignatures.add(signature)) {
DexAnnotationSet annotationSet =
@@ -906,6 +908,8 @@
public void visitEnd() {
InternalOptions options = parent.application.options;
parent.application.checkMethodForRecord(method, parent.classKind);
+ parent.application.checkMethodForMethodHandlesLookup(method, parent.classKind);
+ parent.application.checkMethodForVarHandle(method, parent.classKind);
if (!flags.isAbstract() && !flags.isNative() && classRequiresCode()) {
code = new LazyCfCode(parent.origin, parent.context, parent.application);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java
index 2e95917..5460c34 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringCollection.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterL8Synthesizer;
import com.android.tools.r8.ir.desugar.itf.ProgramEmulatedInterfaceSynthesizer;
import com.android.tools.r8.ir.desugar.records.RecordDesugaring;
+import com.android.tools.r8.ir.desugar.varhandle.VarHandleDesugaring;
import com.android.tools.r8.utils.ThreadUtils;
import java.util.ArrayList;
import java.util.Collection;
@@ -38,6 +39,10 @@
if (recordRewriter != null) {
synthesizers.add(recordRewriter);
}
+ VarHandleDesugaring varHandleDesugaring = VarHandleDesugaring.create(appView);
+ if (varHandleDesugaring != null) {
+ synthesizers.add(varHandleDesugaring);
+ }
if (synthesizers.isEmpty()) {
return new EmptyCfClassSynthesizerCollection();
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java
index 6962d3e..dfaa50d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterL8SynthesizerEventConsumer;
import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceSynthesizerEventConsumer.L8ProgramEmulatedInterfaceSynthesizerEventConsumer;
import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.varhandle.VarHandleDesugaringEventConsumer;
import com.google.common.collect.Sets;
import java.util.Set;
@@ -17,7 +18,8 @@
implements L8ProgramEmulatedInterfaceSynthesizerEventConsumer,
DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer,
DesugaredLibraryRetargeterL8SynthesizerEventConsumer,
- RecordDesugaringEventConsumer {
+ RecordDesugaringEventConsumer,
+ VarHandleDesugaringEventConsumer {
private Set<DexProgramClass> synthesizedClasses = Sets.newConcurrentHashSet();
@@ -46,6 +48,11 @@
synthesizedClasses.add(clazz);
}
+ @Override
+ public void acceptVarHandleDesugaringClass(DexProgramClass clazz) {
+ synthesizedClasses.add(clazz);
+ }
+
public Set<DexProgramClass> getSynthesizedClasses() {
return synthesizedClasses;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
index e44467e..df1ba99 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -28,6 +28,7 @@
import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer.RecordInstructionDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.twr.TwrCloseResourceDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.varhandle.VarHandleDesugaringEventConsumer;
import com.android.tools.r8.shaking.Enqueuer.SyntheticAdditions;
import com.android.tools.r8.shaking.KeepMethodInfo.Joiner;
import com.google.common.collect.Sets;
@@ -59,7 +60,8 @@
DesugaredLibraryRetargeterInstructionEventConsumer,
DesugaredLibraryAPIConverterEventConsumer,
ClasspathEmulatedInterfaceSynthesizerEventConsumer,
- ApiInvokeOutlinerDesugaringEventConsumer {
+ ApiInvokeOutlinerDesugaringEventConsumer,
+ VarHandleDesugaringEventConsumer {
public static D8CfInstructionDesugaringEventConsumer createForD8(
D8MethodProcessor methodProcessor) {
@@ -152,6 +154,11 @@
}
@Override
+ public void acceptVarHandleDesugaringClass(DexProgramClass clazz) {
+ methodProcessor.scheduleDesugaredMethodsForProcessing(clazz.programMethods());
+ }
+
+ @Override
public void acceptLambdaClass(LambdaClass lambdaClass, ProgramMethod context) {
synchronized (synthesizedLambdaClasses) {
synthesizedLambdaClasses.add(lambdaClass);
@@ -353,6 +360,11 @@
}
@Override
+ public void acceptVarHandleDesugaringClass(DexProgramClass clazz) {
+ // Intentionally empty. The class will be hit by tracing if required.
+ }
+
+ @Override
public void acceptCollectionConversion(ProgramMethod arrayConversion) {
// Intentionally empty. The method will be hit by tracing if required.
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index 54837a8..20e5a46 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -31,6 +31,7 @@
import com.android.tools.r8.ir.desugar.records.RecordDesugaring;
import com.android.tools.r8.ir.desugar.stringconcat.StringConcatInstructionDesugaring;
import com.android.tools.r8.ir.desugar.twr.TwrInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.varhandle.VarHandleDesugaring;
import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.utils.IntBox;
import com.android.tools.r8.utils.ListUtils;
@@ -143,6 +144,10 @@
if (recordRewriter != null) {
desugarings.add(recordRewriter);
}
+ VarHandleDesugaring varHandleDesugaring = VarHandleDesugaring.create(appView);
+ if (varHandleDesugaring != null) {
+ desugarings.add(varHandleDesugaring);
+ }
yieldingDesugarings.add(new UnrepresentableInDexInstructionRemover(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
new file mode 100644
index 0000000..9ed7493
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java
@@ -0,0 +1,496 @@
+// 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.ir.desugar.varhandle;
+
+import com.android.tools.r8.cf.code.CfCheckCast;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfNew;
+import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
+import com.android.tools.r8.cf.code.CfStore;
+import com.android.tools.r8.contexts.CompilationContext.ClassSynthesisDesugaringContext;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.errors.MissingGlobalSyntheticsConsumerDiagnostic;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexApplicationReadFlags;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaring;
+import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.DesugarDescription;
+import com.android.tools.r8.ir.desugar.FreshLocalProvider;
+import com.android.tools.r8.ir.desugar.LocalStackAllocator;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.objectweb.asm.Opcodes;
+
+public class VarHandleDesugaring implements CfInstructionDesugaring, CfClassSynthesizerDesugaring {
+
+ private final AppView<?> appView;
+ private final DexItemFactory factory;
+
+ public static VarHandleDesugaring create(AppView<?> appView) {
+ return appView.options().shouldDesugarVarHandle() ? new VarHandleDesugaring(appView) : null;
+ }
+
+ public static void registerSynthesizedCodeReferences(DexItemFactory factory) {
+ VarHandleDesugaringMethods.registerSynthesizedCodeReferences(factory);
+ factory.createSynthesizedType(DexItemFactory.desugarMethodHandlesLookupDescriptorString);
+ }
+
+ public VarHandleDesugaring(AppView<?> appView) {
+ this.appView = appView;
+ this.factory = appView.dexItemFactory();
+ }
+
+ @Override
+ public void scan(
+ ProgramMethod programMethod, CfInstructionDesugaringEventConsumer eventConsumer) {
+ if (programMethod.getHolderType() == factory.desugarVarHandleType) {
+ return;
+ }
+ CfCode cfCode = programMethod.getDefinition().getCode().asCfCode();
+ for (CfInstruction instruction : cfCode.getInstructions()) {
+ scanInstruction(instruction, eventConsumer, programMethod);
+ }
+ }
+
+ private void scanInstruction(
+ CfInstruction instruction,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod context) {
+ assert !instruction.isInitClass();
+ if (instruction.isInvoke()) {
+ CfInvoke cfInvoke = instruction.asInvoke();
+ if (refersToVarHandle(cfInvoke.getMethod(), factory)) {
+ ensureVarHandleClass(eventConsumer, context);
+ }
+ if (refersToMethodHandlesLookup(cfInvoke.getMethod(), factory)) {
+ ensureMethodHandlesLookupClass(eventConsumer, context);
+ }
+ return;
+ }
+ }
+
+ private static boolean refersToVarHandle(DexType type, DexItemFactory factory) {
+ if (type == factory.varHandleType) {
+ // All references to java.lang.invoke.VarHandle is rewritten during application reading.
+ assert false;
+ return true;
+ }
+ return type == factory.desugarVarHandleType;
+ }
+
+ private static boolean refersToVarHandle(DexType[] types, DexItemFactory factory) {
+ for (DexType type : types) {
+ if (refersToVarHandle(type, factory)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean refersToVarHandle(DexMethod method, DexItemFactory factory) {
+ if (refersToVarHandle(method.holder, factory)) {
+ return true;
+ }
+ return refersToVarHandle(method.proto, factory);
+ }
+
+ private static boolean refersToVarHandle(DexProto proto, DexItemFactory factory) {
+ if (refersToVarHandle(proto.returnType, factory)) {
+ return true;
+ }
+ return refersToVarHandle(proto.parameters.values, factory);
+ }
+
+ public static boolean refersToVarHandle(DexField field, DexItemFactory factory) {
+ if (refersToVarHandle(field.holder, factory)) {
+ assert false : "The VarHandle class has no fields.";
+ return true;
+ }
+ return refersToVarHandle(field.type, factory);
+ }
+
+ private static boolean refersToMethodHandlesLookup(DexType type, DexItemFactory factory) {
+ if (type == factory.methodHandlesLookupType) {
+ // All references to java.lang.invoke.MethodHandles$Lookup is rewritten during application
+ // reading.
+ assert false;
+ return true;
+ }
+ return type == factory.desugarMethodHandlesLookupType;
+ }
+
+ private static boolean refersToMethodHandlesLookup(DexType[] types, DexItemFactory factory) {
+ for (DexType type : types) {
+ if (refersToMethodHandlesLookup(type, factory)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean refersToMethodHandlesLookup(DexMethod method, DexItemFactory factory) {
+ if (refersToMethodHandlesLookup(method.holder, factory)) {
+ return true;
+ }
+ return refersToMethodHandlesLookup(method.proto, factory);
+ }
+
+ private static boolean refersToMethodHandlesLookup(DexProto proto, DexItemFactory factory) {
+ if (refersToMethodHandlesLookup(proto.returnType, factory)) {
+ return true;
+ }
+ return refersToMethodHandlesLookup(proto.parameters.values, factory);
+ }
+
+ public static boolean refersToMethodHandlesLookup(DexField field, DexItemFactory factory) {
+ if (refersToMethodHandlesLookup(field.holder, factory)) {
+ assert false : "The MethodHandles$Lookup class has no fields.";
+ return true;
+ }
+ return refersToMethodHandlesLookup(field.type, factory);
+ }
+
+ private void ensureMethodHandlesLookupClass(
+ VarHandleDesugaringEventConsumer eventConsumer, Collection<ProgramDefinition> contexts) {
+ appView
+ .getSyntheticItems()
+ .ensureGlobalClass(
+ () -> new MissingGlobalSyntheticsConsumerDiagnostic("VarHandle desugaring"),
+ kinds -> kinds.METHOD_HANDLES_LOOKUP,
+ factory.desugarMethodHandlesLookupType,
+ contexts,
+ appView,
+ builder ->
+ VarHandleDesugaringMethods.generateDesugarMethodHandlesLookupClass(
+ builder, appView.dexItemFactory()),
+ eventConsumer::acceptVarHandleDesugaringClass);
+ }
+
+ private void ensureMethodHandlesLookupClass(
+ VarHandleDesugaringEventConsumer eventConsumer, ProgramDefinition context) {
+ ensureMethodHandlesLookupClass(eventConsumer, ImmutableList.of(context));
+ }
+
+ private void ensureVarHandleClass(
+ VarHandleDesugaringEventConsumer eventConsumer, Collection<ProgramDefinition> contexts) {
+ appView
+ .getSyntheticItems()
+ .ensureGlobalClass(
+ () -> new MissingGlobalSyntheticsConsumerDiagnostic("VarHandle desugaring"),
+ kinds -> kinds.VAR_HANDLE,
+ factory.desugarVarHandleType,
+ contexts,
+ appView,
+ builder ->
+ VarHandleDesugaringMethods.generateDesugarVarHandleClass(
+ builder, appView.dexItemFactory()),
+ eventConsumer::acceptVarHandleDesugaringClass);
+ }
+
+ private void ensureVarHandleClass(
+ VarHandleDesugaringEventConsumer eventConsumer, ProgramDefinition context) {
+ ensureVarHandleClass(eventConsumer, ImmutableList.of(context));
+ }
+
+ @Override
+ public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
+ return computeDescription(instruction, context).needsDesugaring();
+ }
+
+ @Override
+ public Collection<CfInstruction> desugarInstruction(
+ CfInstruction instruction,
+ FreshLocalProvider freshLocalProvider,
+ LocalStackAllocator localStackAllocator,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod context,
+ MethodProcessingContext methodProcessingContext,
+ CfInstructionDesugaringCollection desugaringCollection,
+ DexItemFactory dexItemFactory) {
+ return computeDescription(instruction, context)
+ .desugarInstruction(
+ freshLocalProvider,
+ localStackAllocator,
+ eventConsumer,
+ context,
+ methodProcessingContext,
+ dexItemFactory);
+ }
+
+ private DesugarDescription computeDescription(CfInstruction instruction, ProgramMethod context) {
+ if (!instruction.isInvoke()) {
+ return DesugarDescription.nothing();
+ }
+ CfInvoke invoke = instruction.asInvoke();
+ DexType holder = invoke.getMethod().getHolderType();
+ if (holder != factory.methodHandlesType
+ && holder != factory.methodHandlesLookupType
+ && holder != factory.desugarVarHandleType) {
+ return DesugarDescription.nothing();
+ }
+ DexMethod method = invoke.getMethod();
+ if (method.getHolderType() == factory.methodHandlesType) {
+ if (method.getName().equals(factory.createString("lookup"))
+ && method.getReturnType() == factory.desugarMethodHandlesLookupType
+ && method.getArity() == 0
+ && invoke.isInvokeStatic()) {
+ return computeMethodHandlesLookup(factory);
+ } else {
+ return DesugarDescription.nothing();
+ }
+ }
+
+ if (method.getHolderType() == factory.methodHandlesLookupType) {
+ assert invoke.isInvokeVirtual();
+
+ if (invoke.getMethod().getReturnType().equals(factory.desugarVarHandleType)) {
+ return computeInvokeMethodHandleLookupMethodReturningVarHandle(factory, invoke);
+ } else {
+ assert invoke.getMethod().getReturnType().equals(factory.methodHandleType);
+ return computeInvokeMethodHandleLookupMethodReturningMethodHandle(factory, invoke);
+ }
+ }
+
+ if (method.getHolderType() == factory.desugarVarHandleType) {
+ assert invoke.isInvokeVirtual();
+ DexString name = method.getName();
+ int arity = method.getProto().getArity();
+ // TODO(b/247076137): Support two coordinates (array element VarHandle).
+ if (name.equals(factory.compareAndSetString)) {
+ assert arity == 3;
+ return computeDesugarSignaturePolymorphicMethod(invoke, arity - 2);
+ } else if (name.equals(factory.getString)) {
+ assert arity == 1;
+ return computeDesugarSignaturePolymorphicMethod(invoke, arity);
+ } else if (name.equals(factory.setString)) {
+ assert arity == 2;
+ return computeDesugarSignaturePolymorphicMethod(invoke, arity - 1);
+ } else {
+ // TODO(b/247076137): Insert runtime exception - unsupported VarHandle operation.
+ return DesugarDescription.nothing();
+ }
+ }
+
+ return DesugarDescription.nothing();
+ }
+
+ public DesugarDescription computeMethodHandlesLookup(DexItemFactory factory) {
+ return DesugarDescription.builder()
+ .setDesugarRewrite(
+ (freshLocalProvider,
+ localStackAllocator,
+ eventConsumer,
+ context,
+ methodProcessingContext,
+ dexItemFactory) ->
+ ImmutableList.of(
+ new CfNew(factory.desugarMethodHandlesLookupType),
+ new CfStackInstruction(Opcode.Dup),
+ new CfInvoke(
+ Opcodes.INVOKESPECIAL,
+ factory.createMethod(
+ factory.desugarMethodHandlesLookupType,
+ factory.createProto(factory.voidType),
+ factory.constructorMethodName),
+ false)))
+ .build();
+ }
+
+ public DesugarDescription computeInvokeMethodHandleLookupMethodReturningVarHandle(
+ DexItemFactory factory, CfInvoke invoke) {
+ return DesugarDescription.builder()
+ .setDesugarRewrite(
+ (freshLocalProvider,
+ localStackAllocator,
+ eventConsumer,
+ context,
+ methodProcessingContext,
+ dexItemFactory) ->
+ ImmutableList.of(
+ new CfInvoke(
+ Opcodes.INVOKEVIRTUAL,
+ factory.createMethod(
+ factory.desugarMethodHandlesLookupType,
+ factory.createProto(
+ factory.desugarVarHandleType,
+ invoke.getMethod().getProto().getParameters()),
+ invoke.getMethod().getName()),
+ false)))
+ .build();
+ }
+
+ public DesugarDescription computeInvokeMethodHandleLookupMethodReturningMethodHandle(
+ DexItemFactory factory, CfInvoke invoke) {
+ return DesugarDescription.builder()
+ .setDesugarRewrite(
+ (freshLocalProvider,
+ localStackAllocator,
+ eventConsumer,
+ context,
+ methodProcessingContext,
+ dexItemFactory) ->
+ ImmutableList.of(
+ new CfInvoke(
+ Opcodes.INVOKEVIRTUAL,
+ factory.createMethod(
+ factory.desugarMethodHandlesLookupType,
+ invoke.getMethod().getProto(),
+ invoke.getMethod().getName()),
+ false)))
+ .build();
+ }
+
+ public DesugarDescription computeDesugarSignaturePolymorphicMethod(
+ CfInvoke invoke, int coordinates) {
+ return DesugarDescription.builder()
+ .setDesugarRewrite(
+ (freshLocalProvider,
+ localStackAllocator,
+ eventConsumer,
+ context,
+ methodProcessingContext,
+ dexItemFactory) ->
+ desugarSignaturePolymorphicMethod(invoke, coordinates, freshLocalProvider))
+ .build();
+ }
+
+ private boolean isPrimitiveThatIsNotBoxed(DexType type) {
+ return type.isIntType() || type.isLongType();
+ }
+
+ private DexType objectOrPrimitiveReturnType(DexType type) {
+ return type.isPrimitiveType() || type.isVoidType() ? type : factory.objectType;
+ }
+
+ private DexType objectOrPrimitiveParameterType(DexType type) {
+ return isPrimitiveThatIsNotBoxed(type) || type.isVoidType() ? type : factory.objectType;
+ }
+
+ private Collection<CfInstruction> desugarSignaturePolymorphicMethod(
+ CfInvoke invoke, int coordinates, FreshLocalProvider freshLocalProvider) {
+ assert invoke.isInvokeVirtual();
+ // TODO(b/247076137): Support two coordinates (array element VarHandle).
+ assert coordinates == 1 && invoke.getMethod().getProto().getArity() >= coordinates;
+ // Only support zero, one and two arguments after coordinates.
+ int nonCoordinateArguments = invoke.getMethod().getProto().getArity() - coordinates;
+ assert nonCoordinateArguments <= 2;
+
+ DexProto proto = invoke.getMethod().getProto();
+ DexType ct1Type = invoke.getMethod().getProto().getParameter(0);
+ if (!ct1Type.isClassType()) {
+ return null;
+ }
+
+ // Convert the arguments by boxing except for primitive int and long.
+ ImmutableList.Builder<CfInstruction> builder = ImmutableList.builder();
+ List<DexType> newParameters = new ArrayList<>(proto.parameters.size());
+ newParameters.add(factory.objectType);
+ if (nonCoordinateArguments > 0) {
+ DexType argumentType = objectOrPrimitiveParameterType(proto.parameters.get(coordinates));
+ boolean hasWideArgument = false;
+ for (int i = coordinates; i < proto.parameters.size(); i++) {
+ hasWideArgument = hasWideArgument || proto.parameters.get(i).isWideType();
+ DexType type = objectOrPrimitiveParameterType(proto.parameters.get(i));
+ if (type != argumentType) {
+ argumentType = factory.objectType;
+ }
+ }
+ assert isPrimitiveThatIsNotBoxed(argumentType) || argumentType == factory.objectType;
+ // Ensure all arguments are boxed.
+ for (int i = coordinates; i < proto.parameters.size(); i++) {
+ if (argumentType.isPrimitiveType()) {
+ newParameters.add(argumentType);
+ } else {
+ boolean lastArgument = i == proto.parameters.size() - 1;
+ // Pass all boxed objects as Object.
+ newParameters.add(factory.objectType);
+ if (!proto.parameters.get(i).isPrimitiveType()) {
+ continue;
+ }
+ int local = -1;
+ // For boxing of the second to last argument (we only have one or two) bring it to TOS.
+ if (!lastArgument) {
+ if (hasWideArgument) {
+ local = freshLocalProvider.getFreshLocal(2);
+ builder.add(new CfStore(ValueType.fromDexType(proto.parameters.get(i + 1)), local));
+ } else {
+ builder.add(new CfStackInstruction(Opcode.Swap));
+ }
+ }
+ builder.add(
+ new CfInvoke(
+ Opcodes.INVOKESTATIC,
+ factory.getBoxPrimitiveMethod(proto.parameters.get(i)),
+ false));
+ // When boxing of the second to last argument (we only have one or two) bring last
+ // argument back to TOS.
+ if (!lastArgument) {
+ if (hasWideArgument) {
+ assert local != -1;
+ builder.add(new CfLoad(ValueType.fromDexType(proto.parameters.get(i + 1)), local));
+ } else {
+ builder.add(new CfStackInstruction(Opcode.Swap));
+ }
+ }
+ }
+ }
+ }
+ assert newParameters.size() == proto.parameters.size();
+ // TODO(b/247076137): Also convert return type if reference type and not Object?.
+ DexProto newProto =
+ factory.createProto(objectOrPrimitiveReturnType(proto.returnType), newParameters);
+ DexMethod newMethod =
+ factory.createMethod(factory.desugarVarHandleType, newProto, invoke.getMethod().getName());
+ builder.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, newMethod, false));
+ if (proto.returnType.isClassType()
+ && proto.returnType != factory.objectType
+ && proto.returnType != factory.voidType) {
+ builder.add(new CfCheckCast(proto.returnType));
+ }
+ return builder.build();
+ }
+
+ @Override
+ public String uniqueIdentifier() {
+ return "$varhandle";
+ }
+
+ @Override
+ // TODO(b/247076137): Is synthesizeClasses needed? Can DesugarVarHandle be created during
+ // desugaring instead?
+ public void synthesizeClasses(
+ ClassSynthesisDesugaringContext processingContext,
+ CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
+ DexApplicationReadFlags flags = appView.appInfo().app().getFlags();
+ if (flags.hasReadVarHandleReferenceFromProgramClass()) {
+ List<ProgramDefinition> classes = new ArrayList<>();
+ for (DexType varHandleWitness : flags.getVarHandleWitnesses()) {
+ DexClass dexClass = appView.contextIndependentDefinitionFor(varHandleWitness);
+ assert dexClass != null;
+ assert dexClass.isProgramClass();
+ classes.add(dexClass.asProgramClass());
+ }
+ ensureVarHandleClass(eventConsumer, classes);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringEventConsumer.java
new file mode 100644
index 0000000..8251809
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaringEventConsumer.java
@@ -0,0 +1,11 @@
+// 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.ir.desugar.varhandle;
+
+import com.android.tools.r8.graph.DexProgramClass;
+
+public interface VarHandleDesugaringEventConsumer {
+
+ void acceptVarHandleDesugaringClass(DexProgramClass varHandleClass);
+}
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 2daf329..78f3611 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
@@ -13,16 +13,25 @@
import com.android.tools.r8.cf.code.CfConstNull;
import com.android.tools.r8.cf.code.CfConstNumber;
import com.android.tools.r8.cf.code.CfConstString;
+import com.android.tools.r8.cf.code.CfFrame;
+import com.android.tools.r8.cf.code.CfGoto;
+import com.android.tools.r8.cf.code.CfIf;
+import com.android.tools.r8.cf.code.CfIfCmp;
import com.android.tools.r8.cf.code.CfInstanceFieldRead;
import com.android.tools.r8.cf.code.CfInstanceFieldWrite;
+import com.android.tools.r8.cf.code.CfInstanceOf;
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.cf.code.CfLabel;
import com.android.tools.r8.cf.code.CfLoad;
import com.android.tools.r8.cf.code.CfNew;
+import com.android.tools.r8.cf.code.CfNumberConversion;
import com.android.tools.r8.cf.code.CfReturn;
import com.android.tools.r8.cf.code.CfReturnVoid;
import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.cf.code.CfStaticFieldRead;
import com.android.tools.r8.cf.code.CfStore;
+import com.android.tools.r8.cf.code.CfThrow;
+import com.android.tools.r8.cf.code.frame.FrameType;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexEncodedField;
@@ -31,14 +40,22 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.FieldAccessFlags;
import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.ir.code.If;
+import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
import com.google.common.collect.ImmutableList;
+import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap;
public final class VarHandleDesugaringMethods {
public static void registerSynthesizedCodeReferences(DexItemFactory factory) {
factory.createSynthesizedType("Lcom/android/tools/r8/DesugarVarHandle;");
+ factory.createSynthesizedType("Ljava/lang/Byte;");
+ factory.createSynthesizedType("Ljava/lang/Integer;");
+ factory.createSynthesizedType("Ljava/lang/Long;");
+ factory.createSynthesizedType("Ljava/lang/RuntimeException;");
+ factory.createSynthesizedType("Ljava/lang/Short;");
factory.createSynthesizedType("Ljava/lang/reflect/Field;");
factory.createSynthesizedType("Lsun/misc/Unsafe;");
}
@@ -81,43 +98,33 @@
.setAccessFlags(FieldAccessFlags.createPublicFinalSynthetic())
.disableAndroidApiLevelCheck()
.build()));
- DexMethod set =
- factory.createMethod(
- builder.getType(),
- factory.createProto(factory.voidType, factory.objectType, factory.objectType),
- factory.createString("set"));
DexMethod get =
factory.createMethod(
builder.getType(),
factory.createProto(factory.objectType, factory.objectType),
factory.createString("get"));
- DexMethod compareAndSetInt =
- factory.createMethod(
- builder.getType(),
- factory.createProto(
- factory.booleanType, factory.objectType, factory.intType, factory.intType),
- factory.createString("compareAndSet"));
- DexMethod getInt =
- factory.createMethod(
- builder.getType(),
- factory.createProto(factory.intType, factory.objectType),
- factory.createString("get"));
DexMethod compareAndSet =
factory.createMethod(
builder.getType(),
factory.createProto(
factory.booleanType, factory.objectType, factory.objectType, factory.objectType),
factory.createString("compareAndSet"));
+ DexMethod constructor_1 =
+ factory.createMethod(
+ builder.getType(),
+ factory.createProto(
+ factory.voidType, factory.createType(factory.createString("Ljava/lang/Class;"))),
+ factory.createString("<init>"));
DexMethod setInt =
factory.createMethod(
builder.getType(),
factory.createProto(factory.voidType, factory.objectType, factory.intType),
factory.createString("set"));
- DexMethod getLong =
+ DexMethod toLongIfPossible =
factory.createMethod(
builder.getType(),
factory.createProto(factory.longType, factory.objectType),
- factory.createString("get"));
+ factory.createString("toLongIfPossible"));
DexMethod constructor_3 =
factory.createMethod(
builder.getType(),
@@ -138,9 +145,49 @@
factory.createProto(
factory.booleanType, factory.objectType, factory.longType, factory.longType),
factory.createString("compareAndSet"));
+ DexMethod set =
+ factory.createMethod(
+ builder.getType(),
+ factory.createProto(factory.voidType, factory.objectType, factory.objectType),
+ factory.createString("set"));
+ DexMethod compareAndSetInt =
+ factory.createMethod(
+ builder.getType(),
+ factory.createProto(
+ factory.booleanType, factory.objectType, factory.intType, factory.intType),
+ factory.createString("compareAndSet"));
+ DexMethod desugarWrongMethodTypeException =
+ factory.createMethod(
+ builder.getType(),
+ factory.createProto(
+ factory.createType(factory.createString("Ljava/lang/RuntimeException;"))),
+ factory.createString("desugarWrongMethodTypeException"));
+ DexMethod getInt =
+ factory.createMethod(
+ 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(),
+ factory.createProto(factory.longType, factory.objectType),
+ factory.createString("get"));
builder.setDirectMethods(
ImmutableList.of(
DexEncodedMethod.syntheticBuilder()
+ .setMethod(constructor_1)
+ .setAccessFlags(
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC, true))
+ .setCode(DesugarVarHandle_constructor_1(factory, constructor_1))
+ .disableAndroidApiLevelCheck()
+ .build(),
+ DexEncodedMethod.syntheticBuilder()
.setMethod(constructor_3)
.setAccessFlags(
MethodAccessFlags.fromSharedAccessFlags(
@@ -151,14 +198,6 @@
builder.setVirtualMethods(
ImmutableList.of(
DexEncodedMethod.syntheticBuilder()
- .setMethod(set)
- .setAccessFlags(
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC, false))
- .setCode(DesugarVarHandle_set(factory, set))
- .disableAndroidApiLevelCheck()
- .build(),
- DexEncodedMethod.syntheticBuilder()
.setMethod(get)
.setAccessFlags(
MethodAccessFlags.fromSharedAccessFlags(
@@ -167,22 +206,6 @@
.disableAndroidApiLevelCheck()
.build(),
DexEncodedMethod.syntheticBuilder()
- .setMethod(compareAndSetInt)
- .setAccessFlags(
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC, false))
- .setCode(DesugarVarHandle_compareAndSetInt(factory, compareAndSetInt))
- .disableAndroidApiLevelCheck()
- .build(),
- DexEncodedMethod.syntheticBuilder()
- .setMethod(getInt)
- .setAccessFlags(
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC, false))
- .setCode(DesugarVarHandle_getInt(factory, getInt))
- .disableAndroidApiLevelCheck()
- .build(),
- DexEncodedMethod.syntheticBuilder()
.setMethod(compareAndSet)
.setAccessFlags(
MethodAccessFlags.fromSharedAccessFlags(
@@ -199,11 +222,11 @@
.disableAndroidApiLevelCheck()
.build(),
DexEncodedMethod.syntheticBuilder()
- .setMethod(getLong)
+ .setMethod(toLongIfPossible)
.setAccessFlags(
MethodAccessFlags.fromSharedAccessFlags(
Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC, false))
- .setCode(DesugarVarHandle_getLong(factory, getLong))
+ .setCode(DesugarVarHandle_toLongIfPossible(factory, toLongIfPossible))
.disableAndroidApiLevelCheck()
.build(),
DexEncodedMethod.syntheticBuilder()
@@ -221,6 +244,56 @@
Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC, false))
.setCode(DesugarVarHandle_compareAndSetLong(factory, compareAndSetLong))
.disableAndroidApiLevelCheck()
+ .build(),
+ DexEncodedMethod.syntheticBuilder()
+ .setMethod(set)
+ .setAccessFlags(
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC, false))
+ .setCode(DesugarVarHandle_set(factory, set))
+ .disableAndroidApiLevelCheck()
+ .build(),
+ DexEncodedMethod.syntheticBuilder()
+ .setMethod(compareAndSetInt)
+ .setAccessFlags(
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC, false))
+ .setCode(DesugarVarHandle_compareAndSetInt(factory, compareAndSetInt))
+ .disableAndroidApiLevelCheck()
+ .build(),
+ DexEncodedMethod.syntheticBuilder()
+ .setMethod(desugarWrongMethodTypeException)
+ .setAccessFlags(
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC, false))
+ .setCode(
+ DesugarVarHandle_desugarWrongMethodTypeException(
+ factory, desugarWrongMethodTypeException))
+ .disableAndroidApiLevelCheck()
+ .build(),
+ DexEncodedMethod.syntheticBuilder()
+ .setMethod(getInt)
+ .setAccessFlags(
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC, false))
+ .setCode(DesugarVarHandle_getInt(factory, getInt))
+ .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(
+ Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC, false))
+ .setCode(DesugarVarHandle_getLong(factory, getLong))
+ .disableAndroidApiLevelCheck()
.build()));
}
@@ -316,6 +389,126 @@
ImmutableList.of());
}
+ public static CfCode DesugarVarHandle_constructor_1(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();
+ CfLabel label6 = new CfLabel();
+ CfLabel label7 = new CfLabel();
+ CfLabel label8 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 3,
+ 3,
+ ImmutableList.of(
+ label0,
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInvoke(
+ 183,
+ factory.createMethod(
+ factory.objectType,
+ factory.createProto(factory.voidType),
+ factory.createString("<init>")),
+ false),
+ label1,
+ new CfConstClass(factory.createType("Lsun/misc/Unsafe;")),
+ new CfConstString(factory.createString("theUnsafe")),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.classType,
+ factory.createProto(
+ factory.createType("Ljava/lang/reflect/Field;"), factory.stringType),
+ factory.createString("getDeclaredField")),
+ false),
+ new CfStore(ValueType.OBJECT, 2),
+ label2,
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfConstNumber(1, ValueType.INT),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Ljava/lang/reflect/Field;"),
+ factory.createProto(factory.voidType, factory.booleanType),
+ factory.createString("setAccessible")),
+ false),
+ label3,
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfConstNull(),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Ljava/lang/reflect/Field;"),
+ factory.createProto(factory.objectType, factory.objectType),
+ factory.createString("get")),
+ false),
+ new CfCheckCast(factory.createType("Lsun/misc/Unsafe;")),
+ new CfInstanceFieldWrite(
+ factory.createField(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("Lsun/misc/Unsafe;"),
+ factory.createString("U"))),
+ label4,
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfInstanceFieldWrite(
+ factory.createField(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.classType,
+ factory.createString("recv"))),
+ label5,
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.classType,
+ factory.createProto(factory.classType),
+ factory.createString("getComponentType")),
+ false),
+ new CfInstanceFieldWrite(
+ factory.createField(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.classType,
+ factory.createString("type"))),
+ label6,
+ 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, 0),
+ new CfInstanceFieldRead(
+ factory.createField(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.classType,
+ factory.createString("recv"))),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lsun/misc/Unsafe;"),
+ factory.createProto(factory.intType, factory.classType),
+ factory.createString("arrayBaseOffset")),
+ false),
+ new CfNumberConversion(NumericType.INT, NumericType.LONG),
+ new CfInstanceFieldWrite(
+ factory.createField(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.longType,
+ factory.createString("offset"))),
+ label7,
+ new CfReturnVoid(),
+ label8),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
public static CfCode DesugarVarHandle_constructor_3(DexItemFactory factory, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
@@ -456,12 +649,189 @@
public static CfCode DesugarVarHandle_compareAndSet(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();
+ CfLabel label6 = new CfLabel();
+ CfLabel label7 = new CfLabel();
+ CfLabel label8 = new CfLabel();
+ CfLabel label9 = new CfLabel();
return new CfCode(
method.holder,
- 1,
+ 8,
4,
ImmutableList.of(
- label0, new CfConstNumber(0, ValueType.INT), new CfReturn(ValueType.INT), label1),
+ label0,
+ 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),
+ 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),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInstanceFieldRead(
+ factory.createField(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.longType,
+ factory.createString("offset"))),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfLoad(ValueType.OBJECT, 2),
+ label2,
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createProto(factory.intType, factory.objectType),
+ factory.createString("toIntIfPossible")),
+ false),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfLoad(ValueType.OBJECT, 3),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createProto(factory.intType, factory.objectType),
+ factory.createString("toIntIfPossible")),
+ false),
+ label3,
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lsun/misc/Unsafe;"),
+ factory.createProto(
+ factory.booleanType,
+ factory.objectType,
+ factory.longType,
+ factory.intType,
+ factory.intType),
+ factory.createString("compareAndSwapInt")),
+ false),
+ new CfReturn(ValueType.INT),
+ 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.initializedNonNullReference(factory.objectType),
+ 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, label8),
+ label5,
+ 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.OBJECT, 0),
+ new CfLoad(ValueType.OBJECT, 2),
+ label6,
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createProto(factory.longType, factory.objectType),
+ factory.createString("toLongIfPossible")),
+ false),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfLoad(ValueType.OBJECT, 3),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createProto(factory.longType, factory.objectType),
+ factory.createString("toLongIfPossible")),
+ false),
+ label7,
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lsun/misc/Unsafe;"),
+ factory.createProto(
+ factory.booleanType,
+ factory.objectType,
+ factory.longType,
+ factory.longType,
+ factory.longType),
+ factory.createString("compareAndSwapLong")),
+ false),
+ new CfReturn(ValueType.INT),
+ label8,
+ 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.initializedNonNullReference(factory.objectType),
+ FrameType.initializedNonNullReference(factory.objectType)
+ })),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInstanceFieldRead(
+ factory.createField(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createType("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.OBJECT, 2),
+ new CfLoad(ValueType.OBJECT, 3),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lsun/misc/Unsafe;"),
+ factory.createProto(
+ factory.booleanType,
+ factory.objectType,
+ factory.longType,
+ factory.objectType,
+ factory.objectType),
+ factory.createString("compareAndSwapObject")),
+ false),
+ new CfReturn(ValueType.INT),
+ label9),
ImmutableList.of(),
ImmutableList.of());
}
@@ -469,12 +839,153 @@
public static CfCode DesugarVarHandle_compareAndSetInt(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,
- 1,
+ 8,
4,
ImmutableList.of(
- label0, new CfConstNumber(0, ValueType.INT), new CfReturn(ValueType.INT), label1),
+ label0,
+ 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, 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),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInstanceFieldRead(
+ factory.createField(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.longType,
+ factory.createString("offset"))),
+ new CfLoad(ValueType.INT, 2),
+ new CfLoad(ValueType.INT, 3),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lsun/misc/Unsafe;"),
+ factory.createProto(
+ factory.booleanType,
+ factory.objectType,
+ factory.longType,
+ factory.intType,
+ factory.intType),
+ factory.createString("compareAndSwapInt")),
+ false),
+ new CfReturn(ValueType.INT),
+ label2,
+ 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.intType(),
+ FrameType.intType()
+ })),
+ 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 CfLoad(ValueType.INT, 2),
+ new CfNumberConversion(NumericType.INT, NumericType.LONG),
+ new CfLoad(ValueType.INT, 3),
+ new CfNumberConversion(NumericType.INT, NumericType.LONG),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lsun/misc/Unsafe;"),
+ factory.createProto(
+ factory.booleanType,
+ factory.objectType,
+ factory.longType,
+ factory.longType,
+ factory.longType),
+ factory.createString("compareAndSwapLong")),
+ false),
+ new CfReturn(ValueType.INT),
+ 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.intType(),
+ FrameType.intType()
+ })),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfLoad(ValueType.INT, 2),
+ new CfInvoke(
+ 184,
+ factory.createMethod(
+ factory.createType("Ljava/lang/Integer;"),
+ factory.createProto(factory.createType("Ljava/lang/Integer;"), factory.intType),
+ factory.createString("valueOf")),
+ false),
+ new CfLoad(ValueType.INT, 3),
+ new CfInvoke(
+ 184,
+ factory.createMethod(
+ factory.createType("Ljava/lang/Integer;"),
+ factory.createProto(factory.createType("Ljava/lang/Integer;"), factory.intType),
+ factory.createString("valueOf")),
+ false),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createProto(
+ factory.booleanType,
+ factory.objectType,
+ factory.objectType,
+ factory.objectType),
+ factory.createString("compareAndSet")),
+ false),
+ new CfReturn(ValueType.INT),
+ label5),
ImmutableList.of(),
ImmutableList.of());
}
@@ -483,12 +994,125 @@
DexItemFactory factory, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
+ CfLabel label2 = new CfLabel();
+ CfLabel label3 = new CfLabel();
return new CfCode(
method.holder,
- 1,
+ 8,
6,
ImmutableList.of(
- label0, new CfConstNumber(0, ValueType.INT), new CfReturn(ValueType.INT), label1),
+ label0,
+ 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, 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),
+ 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 CfLoad(ValueType.LONG, 4),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lsun/misc/Unsafe;"),
+ factory.createProto(
+ factory.booleanType,
+ factory.objectType,
+ factory.longType,
+ factory.longType,
+ factory.longType),
+ factory.createString("compareAndSwapLong")),
+ false),
+ new CfReturn(ValueType.INT),
+ label2,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1, 2, 3, 4, 5},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;")),
+ FrameType.initializedNonNullReference(factory.objectType),
+ FrameType.longType(),
+ FrameType.longHighType(),
+ FrameType.longType(),
+ FrameType.longHighType()
+ })),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfLoad(ValueType.OBJECT, 1),
+ 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 CfLoad(ValueType.LONG, 4),
+ 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("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createProto(
+ factory.booleanType,
+ factory.objectType,
+ factory.objectType,
+ factory.objectType),
+ factory.createString("compareAndSet")),
+ false),
+ new CfReturn(ValueType.INT),
+ label3),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
+ public static CfCode DesugarVarHandle_desugarWrongMethodTypeException(
+ DexItemFactory factory, DexMethod method) {
+ CfLabel label0 = new CfLabel();
+ CfLabel label1 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 3,
+ 1,
+ ImmutableList.of(
+ label0,
+ new CfNew(factory.createType("Ljava/lang/RuntimeException;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfConstString(factory.createString("java.lang.invoke.WrongMethodTypeException")),
+ new CfInvoke(
+ 183,
+ factory.createMethod(
+ factory.createType("Ljava/lang/RuntimeException;"),
+ factory.createProto(factory.voidType, factory.stringType),
+ factory.createString("<init>")),
+ false),
+ new CfReturn(ValueType.OBJECT),
+ label1),
ImmutableList.of(),
ImmutableList.of());
}
@@ -496,11 +1120,138 @@
public static CfCode DesugarVarHandle_get(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,
- 1,
+ 4,
2,
- ImmutableList.of(label0, new CfConstNull(), new CfReturn(ValueType.OBJECT), label1),
+ ImmutableList.of(
+ label0,
+ 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, 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),
+ 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 CfInvoke(
+ 184,
+ factory.createMethod(
+ factory.createType("Ljava/lang/Integer;"),
+ factory.createProto(factory.createType("Ljava/lang/Integer;"), factory.intType),
+ factory.createString("valueOf")),
+ false),
+ new CfReturn(ValueType.OBJECT),
+ 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 CfInvoke(
+ 184,
+ factory.createMethod(
+ factory.createType("Ljava/lang/Long;"),
+ factory.createProto(factory.createType("Ljava/lang/Long;"), factory.longType),
+ factory.createString("valueOf")),
+ false),
+ new CfReturn(ValueType.OBJECT),
+ 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 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 CfReturn(ValueType.OBJECT),
+ label5),
ImmutableList.of(),
ImmutableList.of());
}
@@ -510,10 +1261,32 @@
CfLabel label1 = new CfLabel();
return new CfCode(
method.holder,
- 1,
+ 4,
2,
ImmutableList.of(
- label0, new CfConstNumber(-1, ValueType.INT), new CfReturn(ValueType.INT), label1),
+ label0,
+ 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 CfReturn(ValueType.INT),
+ label1),
ImmutableList.of(),
ImmutableList.of());
}
@@ -534,11 +1307,91 @@
public static CfCode DesugarVarHandle_set(DexItemFactory factory, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
+ CfLabel label2 = new CfLabel();
+ CfLabel label3 = new CfLabel();
+ CfLabel label4 = new CfLabel();
return new CfCode(
method.holder,
- 0,
+ 5,
3,
- ImmutableList.of(label0, new CfReturnVoid(), label1),
+ ImmutableList.of(
+ label0,
+ 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, label2),
+ label1,
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createProto(factory.intType, factory.objectType),
+ factory.createString("toIntIfPossible")),
+ false),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createProto(factory.voidType, factory.objectType, factory.intType),
+ factory.createString("set")),
+ false),
+ new CfGoto(label3),
+ label2,
+ 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.initializedNonNullReference(factory.objectType)
+ })),
+ 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.OBJECT, 2),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lsun/misc/Unsafe;"),
+ factory.createProto(
+ factory.voidType, factory.objectType, factory.longType, factory.objectType),
+ factory.createString("putObject")),
+ false),
+ label3,
+ 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.initializedNonNullReference(factory.objectType)
+ })),
+ new CfReturnVoid(),
+ label4),
ImmutableList.of(),
ImmutableList.of());
}
@@ -546,11 +1399,139 @@
public static CfCode DesugarVarHandle_setInt(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();
+ CfLabel label6 = new CfLabel();
return new CfCode(
method.holder,
- 0,
+ 6,
3,
- ImmutableList.of(label0, new CfReturnVoid(), label1),
+ ImmutableList.of(
+ label0,
+ 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, 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),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInstanceFieldRead(
+ factory.createField(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.longType,
+ factory.createString("offset"))),
+ new CfLoad(ValueType.INT, 2),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lsun/misc/Unsafe;"),
+ factory.createProto(
+ factory.voidType, factory.objectType, factory.longType, factory.intType),
+ factory.createString("putInt")),
+ false),
+ new CfGoto(label5),
+ label2,
+ 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 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 CfLoad(ValueType.INT, 2),
+ new CfNumberConversion(NumericType.INT, NumericType.LONG),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lsun/misc/Unsafe;"),
+ factory.createProto(
+ factory.voidType, factory.objectType, factory.longType, factory.longType),
+ factory.createString("putLong")),
+ false),
+ new CfGoto(label5),
+ label4,
+ 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 CfLoad(ValueType.OBJECT, 1),
+ new CfLoad(ValueType.INT, 2),
+ new CfInvoke(
+ 184,
+ factory.createMethod(
+ factory.createType("Ljava/lang/Integer;"),
+ factory.createProto(factory.createType("Ljava/lang/Integer;"), factory.intType),
+ factory.createString("valueOf")),
+ false),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createProto(factory.voidType, factory.objectType, factory.objectType),
+ factory.createString("set")),
+ false),
+ label5,
+ 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 CfReturnVoid(),
+ label6),
ImmutableList.of(),
ImmutableList.of());
}
@@ -558,11 +1539,257 @@
public static CfCode DesugarVarHandle_setLong(DexItemFactory factory, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
+ CfLabel label2 = new CfLabel();
+ CfLabel label3 = new CfLabel();
+ CfLabel label4 = new CfLabel();
return new CfCode(
method.holder,
- 0,
+ 6,
4,
- ImmutableList.of(label0, new CfReturnVoid(), label1),
+ ImmutableList.of(
+ label0,
+ 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, 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),
+ 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(
+ 182,
+ factory.createMethod(
+ factory.createType("Lsun/misc/Unsafe;"),
+ factory.createProto(
+ factory.voidType, factory.objectType, factory.longType, factory.longType),
+ factory.createString("putLong")),
+ false),
+ new CfGoto(label3),
+ label2,
+ 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 CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createProto(factory.createType("Ljava/lang/RuntimeException;")),
+ factory.createString("desugarWrongMethodTypeException")),
+ false),
+ new CfThrow(),
+ label3,
+ 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 CfReturnVoid(),
+ label4),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
+ public static CfCode DesugarVarHandle_toIntIfPossible(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();
+ CfLabel label6 = new CfLabel();
+ CfLabel label7 = new CfLabel();
+ CfLabel label8 = new CfLabel();
+ CfLabel label9 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 1,
+ 2,
+ ImmutableList.of(
+ label0,
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfInstanceOf(factory.createType("Ljava/lang/Integer;")),
+ new CfIf(If.Type.EQ, ValueType.INT, label2),
+ label1,
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfCheckCast(factory.createType("Ljava/lang/Integer;")),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Ljava/lang/Integer;"),
+ factory.createProto(factory.intType),
+ factory.createString("intValue")),
+ false),
+ new CfReturn(ValueType.INT),
+ 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, 1),
+ new CfInstanceOf(factory.createType("Ljava/lang/Byte;")),
+ new CfIf(If.Type.EQ, ValueType.INT, label4),
+ label3,
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfCheckCast(factory.createType("Ljava/lang/Byte;")),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Ljava/lang/Byte;"),
+ factory.createProto(factory.byteType),
+ factory.createString("byteValue")),
+ false),
+ 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, 1),
+ new CfInstanceOf(factory.boxedCharType),
+ new CfIf(If.Type.EQ, ValueType.INT, label6),
+ label5,
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfCheckCast(factory.boxedCharType),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.boxedCharType,
+ factory.createProto(factory.charType),
+ factory.createString("charValue")),
+ false),
+ new CfReturn(ValueType.INT),
+ label6,
+ 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, 1),
+ new CfInstanceOf(factory.createType("Ljava/lang/Short;")),
+ new CfIf(If.Type.EQ, ValueType.INT, label8),
+ label7,
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfCheckCast(factory.createType("Ljava/lang/Short;")),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Ljava/lang/Short;"),
+ factory.createProto(factory.shortType),
+ factory.createString("shortValue")),
+ false),
+ new CfReturn(ValueType.INT),
+ label8,
+ 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 CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createProto(factory.createType("Ljava/lang/RuntimeException;")),
+ factory.createString("desugarWrongMethodTypeException")),
+ false),
+ new CfThrow(),
+ label9),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
+ public static CfCode DesugarVarHandle_toLongIfPossible(DexItemFactory factory, DexMethod method) {
+ CfLabel label0 = new CfLabel();
+ CfLabel label1 = new CfLabel();
+ CfLabel label2 = new CfLabel();
+ CfLabel label3 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 2,
+ 2,
+ ImmutableList.of(
+ label0,
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfInstanceOf(factory.createType("Ljava/lang/Long;")),
+ new CfIf(If.Type.EQ, ValueType.INT, label2),
+ label1,
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfCheckCast(factory.createType("Ljava/lang/Long;")),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Ljava/lang/Long;"),
+ factory.createProto(factory.longType),
+ factory.createString("longValue")),
+ false),
+ new CfReturn(ValueType.LONG),
+ 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 CfLoad(ValueType.OBJECT, 1),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Lcom/android/tools/r8/DesugarVarHandle;"),
+ factory.createProto(factory.intType, factory.objectType),
+ factory.createString("toIntIfPossible")),
+ false),
+ new CfNumberConversion(NumericType.INT, NumericType.LONG),
+ new CfReturn(ValueType.LONG),
+ label3),
ImmutableList.of(),
ImmutableList.of());
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 5002bac..a2cd8e8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -2461,7 +2461,9 @@
Set<BasicBlock> visitedBlocks = Sets.newIdentityHashSet();
for (Instruction instruction : instructionsToRemove) {
BasicBlock ownerBlock = instruction.getBlock();
- if (visitedBlocks.add(ownerBlock)) {
+ // If owner block is null, then the instruction has been removed already. We can't rely on
+ // just having the block pointer nulled, so the visited blocks guards reprocessing.
+ if (ownerBlock != null && visitedBlocks.add(ownerBlock)) {
InstructionListIterator removeIt = ownerBlock.listIterator(code);
while (removeIt.hasNext()) {
if (instructionsToRemove.contains(removeIt.next())) {
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 54ef01b..5e95467 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -354,7 +354,7 @@
assert !prefix.contains(SyntheticNaming.getPhaseSeparator(Phase.INTERNAL));
DexType context =
dexItemFactory.createType(DescriptorUtils.getDescriptorFromClassBinaryName(prefix));
- assert isNotSyntheticType(context);
+ assert isNotSyntheticType(context) || context == dexItemFactory.desugarVarHandleType;
});
return true;
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index 2aa20c2..59aff72 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -27,6 +27,8 @@
// Global synthetics.
public final SyntheticKind RECORD_TAG = generator.forGlobalClass();
public final SyntheticKind API_MODEL_STUB = generator.forGlobalClass();
+ public final SyntheticKind METHOD_HANDLES_LOOKUP = generator.forGlobalClass();
+ public final SyntheticKind VAR_HANDLE = generator.forGlobalClass();
// Classpath only synthetics in the global type namespace.
public final SyntheticKind GENERIC_API_CONVERSION_STUB = generator.forGlobalClasspathClass();
@@ -438,7 +440,8 @@
}
public static boolean verifyNotInternalSynthetic(String typeBinaryNameOrDescriptor) {
- assert !typeBinaryNameOrDescriptor.contains(INTERNAL_SYNTHETIC_CLASS_SEPARATOR);
+ assert !typeBinaryNameOrDescriptor.contains(INTERNAL_SYNTHETIC_CLASS_SEPARATOR)
+ : typeBinaryNameOrDescriptor;
return true;
}
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
index acb142b..d98f497 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -95,6 +95,10 @@
return internalToDescriptor(typeName, false, false);
}
+ public static String javaClassToDescriptor(Class<?> clazz) {
+ return javaTypeToDescriptor(clazz.getTypeName());
+ }
+
/**
* Convert a Java type name to a descriptor string ignoring primitive types.
*
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 4f8669b..38e6b3f 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -601,6 +601,10 @@
return desugarState.isOn() && !canUseRecords();
}
+ public boolean shouldDesugarVarHandle() {
+ return desugarState.isOn() && !canUseVarHandle() && enableVarHandleDesugaring;
+ }
+
public Set<String> extensiveLoggingFilter = getExtensiveLoggingFilter();
public Set<String> extensiveInterfaceMethodMinifierLoggingFilter =
getExtensiveInterfaceMethodMinifierLoggingFilter();
@@ -622,6 +626,8 @@
public boolean enableLoadStoreOptimization = true;
// Flag to turn on/off desugaring in D8/R8.
public DesugarState desugarState = DesugarState.ON;
+ // Flag to turn on/off partial VarHandle desugaring.
+ public boolean enableVarHandleDesugaring = false;
// Flag to turn on/off backport methods.
public boolean enableBackportMethods = true;
// Flag to turn on/off reduction of nest to improve class merging optimizations.
@@ -2351,6 +2357,14 @@
return hasFeaturePresentFrom(privateInterfaceMethodsApiLevel());
}
+ public static AndroidApiLevel varHandleApiLevel() {
+ return AndroidApiLevel.T;
+ }
+
+ public boolean canUseVarHandle() {
+ return hasFeaturePresentFrom(varHandleApiLevel());
+ }
+
public boolean canUseNestBasedAccess() {
return hasFeaturePresentFrom(null) || emitNestAnnotationsInDex;
}
diff --git a/src/main/java/com/android/tools/r8/utils/StringUtils.java b/src/main/java/com/android/tools/r8/utils/StringUtils.java
index 69cef74..a5b9936 100644
--- a/src/main/java/com/android/tools/r8/utils/StringUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/StringUtils.java
@@ -12,6 +12,8 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -375,6 +377,13 @@
return string.length();
}
+ public static String replaceAll(String subject, Map<String, String> map) {
+ for (Entry<String, String> entry : map.entrySet()) {
+ subject = replaceAll(subject, entry.getKey(), entry.getValue());
+ }
+ return subject;
+ }
+
public static String replaceAll(String subject, String target, String replacement) {
return subject.replaceAll(Pattern.quote(target), Matcher.quoteReplacement(replacement));
}
diff --git a/src/test/examplesJava9/varhandle/VarHandleTests.java b/src/test/examplesJava9/varhandle/VarHandleTests.java
index 624843e..c1af388 100644
--- a/src/test/examplesJava9/varhandle/VarHandleTests.java
+++ b/src/test/examplesJava9/varhandle/VarHandleTests.java
@@ -3,16 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
package varhandle;
-import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;
public class VarHandleTests {
private static boolean staticField = true;
- public static void test1() throws NoSuchFieldException, IllegalAccessException {
+ public static void test() throws NoSuchFieldException, IllegalAccessException {
VarHandle vb = MethodHandles.lookup()
.findStaticVarHandle(VarHandleTests.class, "staticField", boolean.class);
System.out.println((boolean) vb.get());
@@ -21,6 +19,6 @@
}
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
- VarHandleTests.test1();
+ VarHandleTests.test();
}
}
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 4d15a0a..9ae8ecd 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
@@ -84,4 +84,9 @@
protected String getExpectedOutput() {
return EXPECTED_OUTPUT;
}
+
+ @Override
+ protected boolean getTestWithDesugaring() {
+ return true;
+ }
}
diff --git a/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringTestBase.java b/src/test/java/com/android/tools/r8/cf/varhandle/VarHandleDesugaringTestBase.java
index 8a5bb2c..6a6c9d5 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
@@ -32,7 +32,9 @@
public static TestParametersCollection data() {
return getTestParameters()
.withCfRuntimesStartingFromIncluding(CfVm.JDK9)
- .withDexRuntimes()
+ // 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.
+ .withDexRuntimesStartingFromExcluding(Version.V4_4_4)
.withAllApiLevels()
.build();
}
@@ -47,6 +49,11 @@
protected abstract String getExpectedOutput();
+ // TODO(b/247076137): Remove this when all tests can run with desugaring.
+ protected boolean getTestWithDesugaring() {
+ return false;
+ }
+
@Test
public void testReference() throws Throwable {
assumeTrue(parameters.isCfRuntime());
@@ -59,34 +66,45 @@
@Test
public void testD8() throws Throwable {
assumeTrue(parameters.isDexRuntime());
- testForD8(parameters.getBackend())
- // Use android.jar from Android T to get the VarHandle type. This is not strictly needed
- // to D8 as it does not fail on missing types.
- // TODO(b/247076137): With desugaring removing VarHandle the type should not be needed in
- // the library and any android.jar should work.
- .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
- .addProgramClassFileData(ZipUtils.readSingleEntry(VarHandle.jar(), getJarEntry()))
- .setMinApi(parameters.getApiLevel())
- .run(parameters.getRuntime(), getMainClass())
- // TODO(b/247076137): Test should pass on all platforms with desugaring implemented.
- .applyIf(
- // VarHandle is available from Android 9, even though it was not a public API until 13.
- parameters.asDexRuntime().getVersion().isOlderThanOrEqual(Version.V7_0_0),
- r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
- parameters.getApiLevel().isLessThan(AndroidApiLevel.P)
- || parameters.asDexRuntime().getVersion().isOlderThanOrEqual(Version.V8_1_0),
- r -> r.assertFailure(),
- r -> r.assertSuccessWithOutput(getExpectedOutput()));
+ if (getTestWithDesugaring()) {
+ testForD8(parameters.getBackend())
+ .addProgramClassFileData(ZipUtils.readSingleEntry(VarHandle.jar(), getJarEntry()))
+ .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.
+ r -> r.assertFailureWithErrorThatThrows(NoSuchFieldException.class),
+ r -> r.assertSuccessWithOutput(getExpectedOutput()));
+ } else {
+ testForD8(parameters.getBackend())
+ .addProgramClassFileData(ZipUtils.readSingleEntry(VarHandle.jar(), getJarEntry()))
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), getMainClass())
+ .applyIf(
+ // VarHandle is available from Android 9, even though it was not a public API until
+ // 13.
+ parameters.asDexRuntime().getVersion().isOlderThanOrEqual(Version.V7_0_0),
+ r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
+ parameters.getApiLevel().isLessThan(AndroidApiLevel.P)
+ || parameters.asDexRuntime().getVersion().isOlderThanOrEqual(Version.V8_1_0),
+ r -> r.assertFailure(),
+ r -> r.assertSuccessWithOutput(getExpectedOutput()));
+ }
}
+ // TODO(b/247076137: Also turn on VarHandle desugaring for R8 tests.
@Test
public void testR8() throws Throwable {
testForR8(parameters.getBackend())
// Use android.jar from Android T to get the VarHandle type.
- // TODO(b/247076137): With desugaring removing VarHandle the type should not be needed in
- // the library and any android.jar should work.
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
.addProgramClassFileData(ZipUtils.readSingleEntry(VarHandle.jar(), getJarEntry()))
+ .addLibraryFiles()
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(getMainClass())
.addKeepRules(getKeepRules())
diff --git a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
index 0cd6b23..f87ec27 100644
--- a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
+++ b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
@@ -358,5 +358,4 @@
printer.println("}");
}
}
-
}
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 7e88943..57c13dd 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
@@ -15,6 +15,55 @@
public long objectFieldOffset(Field f) {
throw new RuntimeException("Stub called.");
}
+
+ public boolean compareAndSwapInt(Object obj, long offset, int expectedValue, int newValue) {
+ throw new RuntimeException("Stub called.");
+ }
+
+ public boolean compareAndSwapLong(Object obj, long offset, long expectedValue, long newValue) {
+ throw new RuntimeException("Stub called.");
+ }
+
+ public boolean compareAndSwapObject(
+ Object receiver, long offset, Object expect, Object update) {
+ throw new RuntimeException("Stub called.");
+ }
+
+ public int getInt(Object obj, long offset) {
+ throw new RuntimeException("Stub called.");
+ }
+
+ public void putInt(Object obj, long offset, int newValue) {
+ throw new RuntimeException("Stub called.");
+ }
+
+ public long getLong(Object obj, long offset) {
+ throw new RuntimeException("Stub called.");
+ }
+
+ public void putLong(Object obj, long offset, long newValue) {
+ throw new RuntimeException("Stub called.");
+ }
+
+ public Object getObject(Object receiver, long offset) {
+ throw new RuntimeException("Stub called.");
+ }
+
+ public void putObject(Object obj, long offset, Object newValue) {
+ throw new RuntimeException("Stub called.");
+ }
+
+ public int getIntVolatile(Object obj, long offset) {
+ throw new RuntimeException("Stub called.");
+ }
+
+ public int arrayBaseOffset(Class<?> clazz) {
+ throw new RuntimeException("Stub called.");
+ }
+
+ public int arrayIndexScale(Class<?> clazz) {
+ throw new RuntimeException("Stub called.");
+ }
}
private final UnsafeStub U;
@@ -33,15 +82,56 @@
this.offset = U.objectFieldOffset(recv.getDeclaredField(name));
}
+ DesugarVarHandle(Class<?> arrayType) throws Exception {
+ Field theUnsafe = UnsafeStub.class.getDeclaredField("theUnsafe");
+ theUnsafe.setAccessible(true);
+ U = (UnsafeStub) theUnsafe.get(null);
+ this.recv = arrayType;
+ this.type = arrayType.getComponentType();
+ this.offset = U.arrayBaseOffset(recv);
+ }
+
+ // Helpers.
+ RuntimeException desugarWrongMethodTypeException() {
+ return new RuntimeException("java.lang.invoke.WrongMethodTypeException");
+ }
+
+ int toIntIfPossible(Object value) {
+ if (value instanceof Integer) {
+ return (Integer) value;
+ }
+ if (value instanceof Byte) {
+ return (Byte) value;
+ }
+ if (value instanceof Character) {
+ return (Character) value;
+ }
+ if (value instanceof Short) {
+ return (Short) value;
+ }
+ throw desugarWrongMethodTypeException();
+ }
+
+ long toLongIfPossible(Object value) {
+ if (value instanceof Long) {
+ return (Long) value;
+ }
+ return toIntIfPossible(value);
+ }
+
// get variants.
Object get(Object ct1) {
- // TODO(b/247076137): Implement.
- return null;
+ if (type == int.class) {
+ return U.getInt(ct1, offset);
+ }
+ if (type == long.class) {
+ return U.getLong(ct1, offset);
+ }
+ return U.getObject(ct1, offset);
}
int getInt(Object ct1) {
- // TODO(b/247076137): Implement.
- return -1;
+ return U.getInt(ct1, offset);
}
long getLong(Object ct1) {
@@ -51,29 +141,57 @@
// set variants.
void set(Object ct1, Object newValue) {
- // TODO(b/247076137): Implement.
+ if (type == int.class) {
+ setInt(ct1, toIntIfPossible(newValue));
+ } else {
+ U.putObject(ct1, offset, newValue);
+ }
}
void setInt(Object ct1, int newValue) {
- // TODO(b/247076137): Implement.
+ if (type == int.class) {
+ U.putInt(ct1, offset, newValue);
+ } else if (type == long.class) {
+ U.putLong(ct1, offset, newValue);
+ } else {
+ set(ct1, newValue);
+ }
}
void setLong(Object ct1, long newValue) {
- // TODO(b/247076137): Implement.
+ if (type == long.class) {
+ U.putLong(ct1, offset, newValue);
+ } else {
+ throw desugarWrongMethodTypeException();
+ }
}
- boolean compareAndSet(Object ct1, Object expectedValue, Object newValue) {
- // TODO(b/247076137): Implement.
- return false;
+ boolean compareAndSet(Object ct1, Object expetedValue, Object newValue) {
+ if (type == int.class) {
+ return U.compareAndSwapInt(
+ ct1, offset, toIntIfPossible(expetedValue), toIntIfPossible((newValue)));
+ }
+ if (type == long.class) {
+ return U.compareAndSwapLong(
+ ct1, offset, toLongIfPossible(expetedValue), toLongIfPossible((newValue)));
+ }
+ return U.compareAndSwapObject(ct1, offset, expetedValue, newValue);
}
- boolean compareAndSetInt(Object ct1, int expectedValue, int newValue) {
- // TODO(b/247076137): Implement.
- return false;
+ boolean compareAndSetInt(Object ct1, int expetedValue, int newValue) {
+ if (type == int.class) {
+ return U.compareAndSwapInt(ct1, offset, expetedValue, newValue);
+ } else if (type == long.class) {
+ return U.compareAndSwapLong(ct1, offset, expetedValue, newValue);
+ } else {
+ return compareAndSet(ct1, expetedValue, newValue);
+ }
}
- boolean compareAndSetLong(Object ct1, long expectedValue, long newValue) {
- // TODO(b/247076137): Implement.
- return false;
+ boolean compareAndSetLong(Object ct1, long expetedValue, long newValue) {
+ if (type == long.class) {
+ return U.compareAndSwapLong(ct1, offset, expetedValue, newValue);
+ }
+ return compareAndSet(ct1, expetedValue, newValue);
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/varhandle/GenerateVarHandleMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/varhandle/GenerateVarHandleMethods.java
index 6ce480b..b72d827 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/varhandle/GenerateVarHandleMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/varhandle/GenerateVarHandleMethods.java
@@ -29,6 +29,7 @@
import java.util.Comparator;
import java.util.List;
import java.util.Map;
+import java.util.function.Function;
import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -96,9 +97,11 @@
// TODO(b/261024278): Share this code.
private class InstructionTypeMapper {
private final Map<DexType, DexType> typeMap;
+ private final Function<String, String> methodNameMap;
- InstructionTypeMapper(Map<DexType, DexType> typeMap) {
+ InstructionTypeMapper(Map<DexType, DexType> typeMap, Function<String, String> methodNameMap) {
this.typeMap = typeMap;
+ this.methodNameMap = methodNameMap;
}
private CfInstruction rewriteInstruction(CfInstruction instruction) {
@@ -124,12 +127,14 @@
String name = method.getName().toString();
DexType holderType = invoke.getMethod().getHolderType();
DexType rewrittenType = typeMap.getOrDefault(holderType, holderType);
+ String rewrittenName =
+ rewrittenType == factory.desugarVarHandleType ? methodNameMap.apply(name) : name;
if (rewrittenType != holderType) {
// TODO(b/261024278): If sharing this code also rewrite signature.
return new CfInvoke(
invoke.getOpcode(),
factory.createMethod(
- rewrittenType, invoke.getMethod().getProto(), factory.createString(name)),
+ rewrittenType, invoke.getMethod().getProto(), factory.createString(rewrittenName)),
invoke.isInterface());
}
return instruction;
@@ -178,7 +183,8 @@
factory.desugarVarHandleType,
factory.createType(
"L" + DesugarVarHandle.class.getTypeName().replace('.', '/') + "$UnsafeStub;"),
- factory.unsafeType));
+ factory.unsafeType),
+ GenerateVarHandleMethods::mapMethodName);
code.setInstructions(
code.getInstructions().stream()
.map(instructionTypeMapper::rewriteInstruction)
@@ -207,15 +213,18 @@
@Override
protected DexEncodedMethod mapMethod(DexEncodedMethod method) {
// Map VarHandle access mode methods to not have the Int/Long postfix.
+ return methodWithName(method, mapMethodName(method.getName().toString()));
+ }
+
+ private static String mapMethodName(String name) {
for (String prefix : ImmutableList.of("get", "set", "compareAndSet")) {
- if (method.getName().startsWith(prefix)) {
- assert method.getName().toString().substring(prefix.length()).equals("Int")
- || method.getName().toString().substring(prefix.length()).equals("Long")
- || method.getName().toString().equals(prefix);
- return methodWithName(method, prefix);
+ if (name.startsWith(prefix)
+ && (name.substring(prefix.length()).equals("Int")
+ || name.substring(prefix.length()).equals("Long"))) {
+ return prefix;
}
}
- return methodWithName(method, method.getName().toString());
+ return name;
}
@Test
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepEdgeAnnotationsTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepEdgeAnnotationsTest.java
index fcdab96..01ec5ac 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepEdgeAnnotationsTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepEdgeAnnotationsTest.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.keepanno.keeprules.KeepRuleExtractor;
import com.android.tools.r8.keepanno.processor.KeepEdgeProcessor;
import com.android.tools.r8.keepanno.testsource.KeepClassAndDefaultConstructorSource;
+import com.android.tools.r8.keepanno.testsource.KeepDependentFieldSource;
import com.android.tools.r8.keepanno.testsource.KeepFieldSource;
import com.android.tools.r8.keepanno.testsource.KeepSourceEdges;
import com.android.tools.r8.references.ClassReference;
@@ -42,7 +43,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
-import org.objectweb.asm.AnnotationVisitor;
@RunWith(Parameterized.class)
public class KeepEdgeAnnotationsTest extends TestBase {
@@ -66,7 +66,10 @@
Paths.get(ToolHelper.BUILD_DIR, "classes", "java", "keepanno");
private static List<Class<?>> getTestClasses() {
- return ImmutableList.of(KeepClassAndDefaultConstructorSource.class, KeepFieldSource.class);
+ return ImmutableList.of(
+ KeepClassAndDefaultConstructorSource.class,
+ KeepFieldSource.class,
+ KeepDependentFieldSource.class);
}
private final TestParameters parameters;
@@ -145,14 +148,9 @@
// Strip out all the annotations to ensure they are actually added again.
byte[] stripped =
transformer(source)
- .addClassTransformer(
- new ClassTransformer() {
- @Override
- public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
- // Ignore all input annotations.
- return null;
- }
- })
+ .removeClassAnnotations()
+ .removeMethodAnnotations()
+ .removeFieldAnnotations()
.transform();
// Manually add in the expected edges again.
byte[] readded =
diff --git a/src/test/java/com/android/tools/r8/keepanno/testsource/KeepDependentFieldSource.java b/src/test/java/com/android/tools/r8/keepanno/testsource/KeepDependentFieldSource.java
new file mode 100644
index 0000000..4a0df32
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/testsource/KeepDependentFieldSource.java
@@ -0,0 +1,41 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.keepanno.testsource;
+
+import com.android.tools.r8.keepanno.annotations.KeepCondition;
+import com.android.tools.r8.keepanno.annotations.KeepEdge;
+import com.android.tools.r8.keepanno.annotations.KeepTarget;
+import java.lang.reflect.Field;
+
+public class KeepDependentFieldSource {
+
+ public static class A {
+
+ public int f;
+
+ public A(int x) {
+ f = x;
+ }
+ }
+
+ // The keep edge is context independent, but natural to place close to the reflection usage.
+ @KeepEdge(
+ preconditions = {
+ // The edge is only needed if the main method that uses reflection is actually present.
+ @KeepCondition(classConstant = KeepDependentFieldSource.class, methodName = "main")
+ },
+ consequences = {
+ // Keep the reflectively accessed field.
+ @KeepTarget(classConstant = KeepDependentFieldSource.A.class, fieldName = "f")
+ })
+ public static void main(String[] args) throws Exception {
+ int x = 42 + args.length;
+ Object o = System.nanoTime() > 0 ? new A(x) : null;
+ Field f = o.getClass().getDeclaredField("f");
+ int y = f.getInt(o);
+ if (x == y) {
+ System.out.println("The values match!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/testsource/KeepSourceEdges.java b/src/test/java/com/android/tools/r8/keepanno/testsource/KeepSourceEdges.java
index 7af9aee..d4c7121 100644
--- a/src/test/java/com/android/tools/r8/keepanno/testsource/KeepSourceEdges.java
+++ b/src/test/java/com/android/tools/r8/keepanno/testsource/KeepSourceEdges.java
@@ -3,17 +3,23 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.keepanno.testsource;
+import com.android.tools.r8.keepanno.ast.KeepCondition;
import com.android.tools.r8.keepanno.ast.KeepConsequences;
+import com.android.tools.r8.keepanno.ast.KeepConsequences.Builder;
import com.android.tools.r8.keepanno.ast.KeepEdge;
import com.android.tools.r8.keepanno.ast.KeepFieldNamePattern;
import com.android.tools.r8.keepanno.ast.KeepFieldPattern;
import com.android.tools.r8.keepanno.ast.KeepItemPattern;
import com.android.tools.r8.keepanno.ast.KeepMethodNamePattern;
import com.android.tools.r8.keepanno.ast.KeepMethodPattern;
+import com.android.tools.r8.keepanno.ast.KeepPreconditions;
import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern;
import com.android.tools.r8.keepanno.ast.KeepTarget;
import com.android.tools.r8.utils.StringUtils;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
import java.util.Set;
/**
@@ -23,22 +29,50 @@
*/
public class KeepSourceEdges {
- public static Set<KeepEdge> getExpectedEdges(Class<?> clazz) {
- if (clazz.equals(KeepClassAndDefaultConstructorSource.class)) {
- return getKeepClassAndDefaultConstructorSourceEdges();
+ private static class SourceData {
+ final Class<?> clazz;
+ final String expected;
+ final Set<KeepEdge> edges;
+
+ public SourceData(Class<?> clazz, String expected, Set<KeepEdge> edges) {
+ this.clazz = clazz;
+ this.expected = expected;
+ this.edges = edges;
}
- if (clazz.equals(KeepFieldSource.class)) {
- return getKeepFieldSourceEdges();
+ }
+
+ private static final List<SourceData> SOURCES = new ArrayList<>();
+
+ static {
+ SOURCES.add(
+ new SourceData(
+ KeepClassAndDefaultConstructorSource.class,
+ getKeepClassAndDefaultConstructorSourceExpected(),
+ getKeepClassAndDefaultConstructorSourceEdges()));
+ SOURCES.add(
+ new SourceData(
+ KeepFieldSource.class, getKeepFieldSourceExpected(), getKeepFieldSourceEdges()));
+ SOURCES.add(
+ new SourceData(
+ KeepDependentFieldSource.class,
+ getKeepDependentFieldSourceExpected(),
+ getKeepDependentFieldSourceEdges()));
+ }
+
+ public static Set<KeepEdge> getExpectedEdges(Class<?> clazz) {
+ for (SourceData source : SOURCES) {
+ if (source.clazz == clazz) {
+ return source.edges;
+ }
}
throw new RuntimeException();
}
public static String getExpected(Class<?> clazz) {
- if (clazz.equals(KeepClassAndDefaultConstructorSource.class)) {
- return getKeepClassAndDefaultConstructorSourceExpected();
- }
- if (clazz.equals(KeepFieldSource.class)) {
- return getKeepFieldSourceExpected();
+ for (SourceData source : SOURCES) {
+ if (source.clazz == clazz) {
+ return source.expected;
+ }
}
throw new RuntimeException();
}
@@ -49,21 +83,8 @@
public static Set<KeepEdge> getKeepClassAndDefaultConstructorSourceEdges() {
Class<?> clazz = KeepClassAndDefaultConstructorSource.A.class;
- // Build the class target.
- KeepQualifiedClassNamePattern name = KeepQualifiedClassNamePattern.exact(clazz.getTypeName());
- KeepItemPattern classItem = KeepItemPattern.builder().setClassPattern(name).build();
- KeepTarget classTarget = KeepTarget.builder().setItem(classItem).build();
- // Build the constructor target.
- KeepMethodPattern constructorMethod =
- KeepMethodPattern.builder().setNamePattern(KeepMethodNamePattern.exact("<init>")).build();
- KeepItemPattern constructorItem =
- KeepItemPattern.builder().setClassPattern(name).setMemberPattern(constructorMethod).build();
- KeepTarget constructorTarget = KeepTarget.builder().setItem(constructorItem).build();
- // The consequet set is the class an its constructor.
- KeepConsequences consequences =
- KeepConsequences.builder().addTarget(classTarget).addTarget(constructorTarget).build();
- KeepEdge edge = KeepEdge.builder().setConsequences(consequences).build();
- return Collections.singleton(edge);
+ return Collections.singleton(
+ mkEdge(mkConsequences(mkTarget(mkClass(clazz)), mkTarget(mkMethod(clazz, "<init>")))));
}
public static String getKeepFieldSourceExpected() {
@@ -71,15 +92,71 @@
}
public static Set<KeepEdge> getKeepFieldSourceEdges() {
- Class<?> clazz = KeepFieldSource.A.class;
+ return Collections.singleton(
+ mkEdge(mkConsequences(mkTarget(mkField(KeepFieldSource.A.class, "f")))));
+ }
+
+ public static String getKeepDependentFieldSourceExpected() {
+ return getKeepFieldSourceExpected();
+ }
+
+ public static Set<KeepEdge> getKeepDependentFieldSourceEdges() {
+ return Collections.singleton(
+ mkDepEdge(
+ mkPreconditions(mkCondition(mkMethod(KeepDependentFieldSource.class, "main"))),
+ mkConsequences(mkTarget(mkField(KeepDependentFieldSource.A.class, "f")))));
+ }
+
+ // Ast helpers.
+
+ static KeepItemPattern mkClass(Class<?> clazz) {
+ KeepQualifiedClassNamePattern name = KeepQualifiedClassNamePattern.exact(clazz.getTypeName());
+ return KeepItemPattern.builder().setClassPattern(name).build();
+ }
+
+ static KeepItemPattern mkMethod(Class<?> clazz, String methodName) {
+ KeepQualifiedClassNamePattern name = KeepQualifiedClassNamePattern.exact(clazz.getTypeName());
+ KeepMethodPattern methodPattern =
+ KeepMethodPattern.builder().setNamePattern(KeepMethodNamePattern.exact(methodName)).build();
+ KeepItemPattern methodItem =
+ KeepItemPattern.builder().setClassPattern(name).setMemberPattern(methodPattern).build();
+ return methodItem;
+ }
+
+ static KeepItemPattern mkField(Class<?> clazz, String fieldName) {
KeepQualifiedClassNamePattern name = KeepQualifiedClassNamePattern.exact(clazz.getTypeName());
KeepFieldPattern fieldPattern =
- KeepFieldPattern.builder().setNamePattern(KeepFieldNamePattern.exact("f")).build();
+ KeepFieldPattern.builder().setNamePattern(KeepFieldNamePattern.exact(fieldName)).build();
KeepItemPattern fieldItem =
KeepItemPattern.builder().setClassPattern(name).setMemberPattern(fieldPattern).build();
- KeepTarget fieldTarget = KeepTarget.builder().setItem(fieldItem).build();
- KeepConsequences consequences = KeepConsequences.builder().addTarget(fieldTarget).build();
- KeepEdge edge = KeepEdge.builder().setConsequences(consequences).build();
- return Collections.singleton(edge);
+ return fieldItem;
+ }
+
+ static KeepTarget mkTarget(KeepItemPattern item) {
+ return KeepTarget.builder().setItem(item).build();
+ }
+
+ static KeepCondition mkCondition(KeepItemPattern item) {
+ return KeepCondition.builder().setItem(item).build();
+ }
+
+ static KeepConsequences mkConsequences(KeepTarget... targets) {
+ Builder builder = KeepConsequences.builder();
+ Arrays.asList(targets).forEach(builder::addTarget);
+ return builder.build();
+ }
+
+ static KeepPreconditions mkPreconditions(KeepCondition... conditions) {
+ KeepPreconditions.Builder builder = KeepPreconditions.builder();
+ Arrays.asList(conditions).forEach(builder::addCondition);
+ return builder.build();
+ }
+
+ static KeepEdge mkEdge(KeepConsequences consequences) {
+ return KeepEdge.builder().setConsequences(consequences).build();
+ }
+
+ static KeepEdge mkDepEdge(KeepPreconditions preconditions, KeepConsequences consequences) {
+ return KeepEdge.builder().setPreconditions(preconditions).setConsequences(consequences).build();
}
}
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 8108730..afc975a 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -25,6 +25,7 @@
import com.android.tools.r8.transformers.MethodTransformer.MethodContext;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.ThrowingConsumer;
+import com.google.common.collect.ImmutableMap;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
@@ -1002,15 +1003,17 @@
public ClassFileTransformer replaceClassDescriptorInMethodInstructions(
String oldDescriptor, String newDescriptor) {
+ return replaceClassDescriptorInMethodInstructions(
+ ImmutableMap.of(oldDescriptor, newDescriptor));
+ }
+
+ public ClassFileTransformer replaceClassDescriptorInMethodInstructions(Map<String, String> map) {
return addMethodTransformer(
new MethodTransformer() {
@Override
public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
super.visitFieldInsn(
- opcode,
- rewriteASMInternalTypeName(owner),
- name,
- replaceAll(descriptor, oldDescriptor, newDescriptor));
+ opcode, rewriteASMInternalTypeName(owner), name, replaceAll(descriptor, map));
}
@Override
@@ -1037,8 +1040,7 @@
public void visitLdcInsn(Object value) {
if (value instanceof Type) {
Type type = (Type) value;
- super.visitLdcInsn(
- Type.getType(replaceAll(type.getDescriptor(), oldDescriptor, newDescriptor)));
+ super.visitLdcInsn(Type.getType(replaceAll(type.getDescriptor(), map)));
} else {
super.visitLdcInsn(value);
}
@@ -1051,7 +1053,7 @@
opcode,
rewriteASMInternalTypeName(owner),
name,
- replaceAll(descriptor, oldDescriptor, newDescriptor),
+ replaceAll(descriptor, map),
isInterface);
}
@@ -1069,8 +1071,7 @@
Object arg = bootstrapMethodArguments[i];
if (arg instanceof Handle) {
Handle oldHandle = (Handle) arg;
- String repl =
- replaceAll("L" + oldHandle.getOwner() + ";", oldDescriptor, newDescriptor);
+ String repl = replaceAll("L" + oldHandle.getOwner() + ";", map);
String newOwner = repl.substring(1, repl.length() - 1);
Handle newHandle =
new Handle(
@@ -1093,9 +1094,7 @@
}
private String rewriteASMInternalTypeName(String type) {
- return Type.getType(
- replaceAll(
- Type.getObjectType(type).getDescriptor(), oldDescriptor, newDescriptor))
+ return Type.getType(replaceAll(Type.getObjectType(type).getDescriptor(), map))
.getInternalName();
}
});
@@ -1463,4 +1462,42 @@
}
});
}
+
+ public ClassFileTransformer removeClassAnnotations() {
+ return addClassTransformer(
+ new ClassTransformer() {
+ @Override
+ public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+ // Ignore all input annotations.
+ return null;
+ }
+ });
+ }
+
+ public ClassFileTransformer removeMethodAnnotations() {
+ return addMethodTransformer(
+ new MethodTransformer() {
+ @Override
+ public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+ return null;
+ }
+ });
+ }
+
+ public ClassFileTransformer removeFieldAnnotations() {
+ return addClassTransformer(
+ new ClassTransformer() {
+ @Override
+ public FieldVisitor visitField(
+ int access, String name, String descriptor, String signature, Object value) {
+ FieldVisitor fv = visitField(access, name, descriptor, signature, value);
+ return new FieldVisitor(ASM_VERSION, fv) {
+ @Override
+ public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+ return null;
+ }
+ };
+ }
+ });
+ }
}