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; + } + }; + } + }); + } }