Merge commit '24cf166186f71f6fba2b3b771738877477f7a7fc' into dev-release
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 9bee3f3..e767011 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -568,7 +568,9 @@
appView.appInfo().app().asDirect().builder();
HorizontalClassMergerGraphLens lens = merger.run(appBuilder);
if (lens != null) {
- appView.rewriteWithLensAndApplication(lens, appBuilder.build());
+ DirectMappedDexApplication app = appBuilder.build();
+ appView.removePrunedClasses(app, appView.horizontallyMergedClasses().getSources());
+ appView.rewriteWithLens(lens);
}
timing.end();
}
@@ -807,11 +809,9 @@
appView.setGraphLens(memberRebindingLens);
// Perform repackaging.
- if (options.isRepackagingEnabled() && options.testing.enableExperimentalRepackaging) {
+ if (options.isRepackagingEnabled()) {
DirectMappedDexApplication.Builder appBuilder =
appView.appInfo().app().asDirect().builder();
- // TODO(b/165783399): We need to deal with non-rebound member references in the writer,
- // possibly by adding a member rebinding lens on top of the repackaging lens.
RepackagingLens lens =
new Repackaging(appView.withLiveness()).run(appBuilder, executorService, timing);
if (lens != null) {
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 6073b28..f39795b 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -28,6 +28,7 @@
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexDebugInfo;
import com.android.tools.r8.graph.DexEncodedArray;
+import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
@@ -45,6 +46,7 @@
import com.android.tools.r8.naming.ProguardMapSupplier.ProguardMapId;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.MainDexClasses;
+import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -457,87 +459,107 @@
private void insertAttributeAnnotations() {
// Convert inner-class attributes to DEX annotations
for (DexProgramClass clazz : appView.appInfo().classes()) {
- EnclosingMethodAttribute enclosingMethod = clazz.getEnclosingMethodAttribute();
- List<InnerClassAttribute> innerClasses = clazz.getInnerClasses();
- if (enclosingMethod == null
- && innerClasses.isEmpty()
- && clazz.getClassSignature().hasNoSignature()) {
- continue;
- }
-
- // EnclosingMember translates directly to an enclosing class/method if present.
- List<DexAnnotation> annotations = new ArrayList<>(2 + innerClasses.size());
- if (enclosingMethod != null) {
- if (enclosingMethod.getEnclosingMethod() != null) {
- annotations.add(
- DexAnnotation.createEnclosingMethodAnnotation(
- enclosingMethod.getEnclosingMethod(), options.itemFactory));
- } else {
- // At this point DEX can't distinguish between local classes and member classes based on
- // the enclosing class annotation itself.
- annotations.add(
- DexAnnotation.createEnclosingClassAnnotation(
- enclosingMethod.getEnclosingClass(), options.itemFactory));
- }
- }
-
- // Each inner-class entry becomes a inner-class (or inner-class & enclosing-class pair) if
- // it relates to the present class. If it relates to the outer-type (and is named) it becomes
- // part of the member-classes annotation.
- if (!innerClasses.isEmpty()) {
- List<DexType> memberClasses = new ArrayList<>(innerClasses.size());
- for (InnerClassAttribute innerClass : innerClasses) {
- if (clazz.type == innerClass.getInner()) {
- if (enclosingMethod == null
- && (innerClass.getOuter() == null || innerClass.isAnonymous())) {
- options.warningMissingEnclosingMember(
- clazz.type, clazz.origin, clazz.getInitialClassFileVersion());
- } else {
- annotations.add(
- DexAnnotation.createInnerClassAnnotation(
- namingLens.lookupInnerName(innerClass, options),
- innerClass.getAccess(),
- options.itemFactory));
- if (innerClass.getOuter() != null && innerClass.isNamed()) {
- annotations.add(
- DexAnnotation.createEnclosingClassAnnotation(
- innerClass.getOuter(), options.itemFactory));
- }
- }
- } else if (clazz.type == innerClass.getOuter() && innerClass.isNamed()) {
- memberClasses.add(innerClass.getInner());
- }
- }
- if (!memberClasses.isEmpty()) {
- annotations.add(
- DexAnnotation.createMemberClassesAnnotation(memberClasses, options.itemFactory));
- }
- }
-
- if (clazz.getClassSignature().hasSignature()) {
- annotations.add(
- DexAnnotation.createSignatureAnnotation(
- clazz.getClassSignature().toRenamedString(namingLens, isTypeMissing),
- options.itemFactory));
- }
-
- if (!annotations.isEmpty()) {
- // Append the annotations to annotations array of the class.
- DexAnnotation[] copy =
- ObjectArrays.concat(
- clazz.annotations().annotations,
- annotations.toArray(DexAnnotation.EMPTY_ARRAY),
- DexAnnotation.class);
- clazz.setAnnotations(new DexAnnotationSet(copy));
- }
-
- // Clear the attribute structures now that they are represented in annotations.
- clazz.clearEnclosingMethodAttribute();
- clazz.clearInnerClasses();
- clazz.clearClassSignature();
+ insertAttributeAnnotationsForClass(clazz);
+ clazz.fields().forEach(this::insertAttributeAnnotationsForField);
}
}
+ private void insertAttributeAnnotationsForClass(DexProgramClass clazz) {
+ EnclosingMethodAttribute enclosingMethod = clazz.getEnclosingMethodAttribute();
+ List<InnerClassAttribute> innerClasses = clazz.getInnerClasses();
+ if (enclosingMethod == null
+ && innerClasses.isEmpty()
+ && clazz.getClassSignature().hasNoSignature()) {
+ return;
+ }
+
+ // EnclosingMember translates directly to an enclosing class/method if present.
+ List<DexAnnotation> annotations = new ArrayList<>(2 + innerClasses.size());
+ if (enclosingMethod != null) {
+ if (enclosingMethod.getEnclosingMethod() != null) {
+ annotations.add(
+ DexAnnotation.createEnclosingMethodAnnotation(
+ enclosingMethod.getEnclosingMethod(), options.itemFactory));
+ } else {
+ // At this point DEX can't distinguish between local classes and member classes based on
+ // the enclosing class annotation itself.
+ annotations.add(
+ DexAnnotation.createEnclosingClassAnnotation(
+ enclosingMethod.getEnclosingClass(), options.itemFactory));
+ }
+ }
+
+ // Each inner-class entry becomes a inner-class (or inner-class & enclosing-class pair) if
+ // it relates to the present class. If it relates to the outer-type (and is named) it becomes
+ // part of the member-classes annotation.
+ if (!innerClasses.isEmpty()) {
+ List<DexType> memberClasses = new ArrayList<>(innerClasses.size());
+ for (InnerClassAttribute innerClass : innerClasses) {
+ if (clazz.type == innerClass.getInner()) {
+ if (enclosingMethod == null
+ && (innerClass.getOuter() == null || innerClass.isAnonymous())) {
+ options.warningMissingEnclosingMember(
+ clazz.type, clazz.origin, clazz.getInitialClassFileVersion());
+ } else {
+ annotations.add(
+ DexAnnotation.createInnerClassAnnotation(
+ namingLens.lookupInnerName(innerClass, options),
+ innerClass.getAccess(),
+ options.itemFactory));
+ if (innerClass.getOuter() != null && innerClass.isNamed()) {
+ annotations.add(
+ DexAnnotation.createEnclosingClassAnnotation(
+ innerClass.getOuter(), options.itemFactory));
+ }
+ }
+ } else if (clazz.type == innerClass.getOuter() && innerClass.isNamed()) {
+ memberClasses.add(innerClass.getInner());
+ }
+ }
+ if (!memberClasses.isEmpty()) {
+ annotations.add(
+ DexAnnotation.createMemberClassesAnnotation(memberClasses, options.itemFactory));
+ }
+ }
+
+ if (clazz.getClassSignature().hasSignature()) {
+ annotations.add(
+ DexAnnotation.createSignatureAnnotation(
+ clazz.getClassSignature().toRenamedString(namingLens, isTypeMissing),
+ options.itemFactory));
+ }
+
+ if (!annotations.isEmpty()) {
+ // Append the annotations to annotations array of the class.
+ DexAnnotation[] copy =
+ ObjectArrays.concat(
+ clazz.annotations().annotations,
+ annotations.toArray(DexAnnotation.EMPTY_ARRAY),
+ DexAnnotation.class);
+ clazz.setAnnotations(new DexAnnotationSet(copy));
+ }
+
+ // Clear the attribute structures now that they are represented in annotations.
+ clazz.clearEnclosingMethodAttribute();
+ clazz.clearInnerClasses();
+ clazz.clearClassSignature();
+ }
+
+ private void insertAttributeAnnotationsForField(DexEncodedField field) {
+ if (field.getFieldSignature().hasNoSignature()) {
+ return;
+ }
+ // Append the annotations to annotations array of the class.
+ field.setAnnotations(
+ new DexAnnotationSet(
+ ArrayUtils.appendSingleElement(
+ field.annotations().annotations,
+ DexAnnotation.createSignatureAnnotation(
+ field.getFieldSignature().toRenamedString(namingLens, isTypeMissing),
+ options.itemFactory))));
+ field.clearFieldSignature();
+ }
+
private void setCallSiteContexts(ExecutorService executorService) throws ExecutionException {
ThreadUtils.processItems(
appView.appInfo().classes(), this::setCallSiteContexts, executorService);
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index 711eb47..6f00265 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -629,14 +629,25 @@
fieldIndex += dexReader.getUleb128();
DexField field = indexedItems.getField(fieldIndex);
FieldAccessFlags accessFlags = FieldAccessFlags.fromDexAccessFlags(dexReader.getUleb128());
- DexAnnotationSet fieldAnnotations = annotationIterator.getNextFor(field);
DexValue staticValue = null;
if (accessFlags.isStatic()) {
if (staticValues != null && i < staticValues.length) {
staticValue = staticValues[i];
}
}
- fields[i] = new DexEncodedField(field, accessFlags, fieldAnnotations, staticValue);
+ DexAnnotationSet fieldAnnotations = annotationIterator.getNextFor(field);
+ String fieldSignature = DexAnnotation.getSignature(fieldAnnotations, dexItemFactory);
+ if (fieldSignature != null) {
+ fieldAnnotations = fieldAnnotations.getWithout(dexItemFactory.annotationSignature);
+ }
+ fields[i] =
+ new DexEncodedField(
+ field,
+ accessFlags,
+ GenericSignature.parseFieldTypeSignature(
+ field.name.toString(), fieldSignature, origin, dexItemFactory, options.reporter),
+ fieldAnnotations,
+ staticValue);
}
return fields;
}
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index e4f0a0c..b808097 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -34,6 +34,7 @@
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.SetUtils;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterators;
import com.google.common.collect.Sets;
@@ -42,6 +43,7 @@
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
@@ -179,12 +181,14 @@
}
private static Map<DexProgramClass, String> computeOriginalNameMapping(
- Collection<DexProgramClass> classes, ClassNameMapper proguardMap) {
- Map<DexProgramClass, String> originalNames = new HashMap<>(classes.size());
+ Collection<DexProgramClass> classes, GraphLens graphLens, ClassNameMapper proguardMap) {
+ Map<DexProgramClass, String> originalNames = new IdentityHashMap<>(classes.size());
classes.forEach(
- (DexProgramClass c) -> {
+ clazz -> {
+ DexType originalType = graphLens.getOriginalType(clazz.getType());
originalNames.put(
- c, DescriptorUtils.descriptorToJavaType(c.type.toDescriptorString(), proguardMap));
+ clazz,
+ DescriptorUtils.descriptorToJavaType(originalType.toDescriptorString(), proguardMap));
});
return originalNames;
}
@@ -367,8 +371,10 @@
virtualFiles.add(mainDexFile);
addMarkers(mainDexFile);
- classes = Sets.newHashSet(appView.appInfo().classes());
- originalNames = computeOriginalNameMapping(classes, appView.appInfo().app().getProguardMap());
+ classes = SetUtils.newIdentityHashSet(appView.appInfo().classes());
+ originalNames =
+ computeOriginalNameMapping(
+ classes, appView.graphLens(), appView.appInfo().app().getProguardMap());
}
private void addMarkers(VirtualFile virtualFile) {
@@ -693,7 +699,7 @@
@Override
public DexString getRenamedDescriptor(DexType type) {
- return namingLens.lookupDescriptor(graphLens.lookupType(type));
+ return namingLens.lookupDescriptor(type);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 282f852..6bf8757 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -35,6 +35,7 @@
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet;
import java.util.Collection;
+import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
@@ -523,6 +524,11 @@
}
public void removePrunedClasses(
+ DirectMappedDexApplication prunedApp, Set<DexType> removedClasses) {
+ removePrunedClasses(prunedApp, removedClasses, Collections.emptySet());
+ }
+
+ public void removePrunedClasses(
DirectMappedDexApplication prunedApp,
Set<DexType> removedClasses,
Collection<DexMethod> methodsToKeepForConfigurationDebugging) {
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index de1c2b9..141484c 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -290,9 +290,11 @@
NamingLens namingLens,
LensCodeRewriterUtils rewriter,
MethodVisitor visitor) {
- assert verifyFrames(method.getDefinition(), appView, null, false);
- DexItemFactory dexItemFactory = appView.dexItemFactory();
GraphLens graphLens = appView.graphLens();
+ // TODO(b/170073151): Handle unapplied code rewritings.
+ assert graphLens.hasCodeRewritings()
+ || verifyFrames(method.getDefinition(), appView, null, false);
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
InitClassLens initClassLens = appView.initClassLens();
InternalOptions options = appView.options();
CfLabel parameterLabel = null;
@@ -410,7 +412,9 @@
Origin origin,
boolean shouldApplyCodeRewritings) {
if (!verifyFrames(method, appView, origin, shouldApplyCodeRewritings)) {
- instructions.removeIf(CfInstruction::isFrame);
+ ArrayList<CfInstruction> copy = new ArrayList<>(instructions);
+ copy.removeIf(CfInstruction::isFrame);
+ setInstructions(copy);
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
index 5bdddd9..05010c8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -243,6 +243,11 @@
return signature.toString();
}
+ public static String getSignature(DexAnnotationSet signatureAnnotations, DexItemFactory factory) {
+ DexAnnotation signature = signatureAnnotations.getFirstMatching(factory.annotationSignature);
+ return signature == null ? null : getSignature(signature);
+ }
+
public static DexAnnotation createThrowsAnnotation(DexValue[] exceptions,
DexItemFactory factory) {
return createSystemValueAnnotation(factory.annotationThrows, factory,
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index 8068417..8e51e49 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -4,10 +4,12 @@
package com.android.tools.r8.graph;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import static com.android.tools.r8.graph.GenericSignature.NO_FIELD_TYPE_SIGNATURE;
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.SingleValue;
@@ -28,6 +30,8 @@
public final FieldAccessFlags accessFlags;
private DexValue staticValue;
private final boolean deprecated;
+ /** Generic signature information if the attribute is present in the input */
+ private FieldTypeSignature fieldSignature;
private FieldOptimizationInfo optimizationInfo = DefaultFieldOptimizationInfo.getInstance();
private KotlinFieldLevelInfo kotlinMemberInfo = NO_KOTLIN_INFO;
@@ -35,6 +39,7 @@
public DexEncodedField(
DexField field,
FieldAccessFlags accessFlags,
+ FieldTypeSignature fieldSignature,
DexAnnotationSet annotations,
DexValue staticValue,
boolean deprecated) {
@@ -43,14 +48,18 @@
this.accessFlags = accessFlags;
this.staticValue = staticValue;
this.deprecated = deprecated;
+ this.fieldSignature = fieldSignature;
+ assert fieldSignature != null;
+ assert GenericSignatureUtils.verifyNoDuplicateGenericDefinitions(fieldSignature, annotations);
}
public DexEncodedField(
DexField field,
FieldAccessFlags accessFlags,
+ FieldTypeSignature fieldSignature,
DexAnnotationSet annotations,
DexValue staticValue) {
- this(field, accessFlags, annotations, staticValue, false);
+ this(field, accessFlags, fieldSignature, annotations, staticValue, false);
}
public DexType type() {
@@ -288,7 +297,9 @@
if (this.field == field) {
return this;
}
- DexEncodedField result = new DexEncodedField(field, accessFlags, annotations(), staticValue);
+ // TODO(b/169923358): Consider removing the fieldSignature here.
+ DexEncodedField result =
+ new DexEncodedField(field, accessFlags, fieldSignature, annotations(), staticValue);
result.optimizationInfo =
optimizationInfo.isMutableFieldOptimizationInfo()
? optimizationInfo.asMutableFieldOptimizationInfo().mutableCopy()
@@ -310,4 +321,17 @@
// TODO(b/150593449): Support non primitive DexValue (String, enum) and add assertions.
return true;
}
+
+ public FieldTypeSignature getFieldSignature() {
+ return fieldSignature;
+ }
+
+ public void setFieldSignature(FieldTypeSignature fieldSignature) {
+ assert fieldSignature != null;
+ this.fieldSignature = fieldSignature;
+ }
+
+ public void clearFieldSignature() {
+ this.fieldSignature = NO_FIELD_TYPE_SIGNATURE;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexField.java b/src/main/java/com/android/tools/r8/graph/DexField.java
index 43c065a..012030b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexField.java
@@ -169,6 +169,10 @@
return dexItemFactory.createField(holder, type, name);
}
+ public DexField withName(DexString name, DexItemFactory dexItemFactory) {
+ return dexItemFactory.createField(holder, type, name);
+ }
+
public FieldReference asFieldReference() {
return Reference.field(
Reference.classFromDescriptor(holder.toDescriptorString()),
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 cdf97ab..cf30eed 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -1799,7 +1799,7 @@
* @param tryString callback to check if the method name is in use.
*/
public <T extends DexMember<?, ?>> T createFreshMember(
- String baseName, DexType holder, Function<DexString, Optional<T>> tryString) {
+ Function<DexString, Optional<T>> tryString, String baseName, DexType holder) {
int index = 0;
while (true) {
DexString name = createString(createMemberString(baseName, holder, index++));
@@ -1811,6 +1811,17 @@
}
/**
+ * Find a fresh method name that is not used by any other method. The method name takes the form
+ * "basename" or "basename$index".
+ *
+ * @param tryString callback to check if the method name is in use.
+ */
+ public <T extends DexMember<?, ?>> T createFreshMember(
+ Function<DexString, Optional<T>> tryString, String baseName) {
+ return createFreshMember(tryString, baseName, null);
+ }
+
+ /**
* Find a fresh method name that is not in the string pool. The name takes the form
* "basename$holdername" or "basename$holdername$index".
*/
@@ -1850,8 +1861,6 @@
Predicate<DexMethod> isFresh) {
DexMethod method =
createFreshMember(
- baseName,
- holder,
name -> {
DexMethod tryMethod = createMethod(target, proto, name);
if (isFresh.test(tryMethod)) {
@@ -1859,7 +1868,9 @@
} else {
return Optional.empty();
}
- });
+ },
+ baseName,
+ holder);
return method;
}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignature.java b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
index 5cd3f8f..bbff32c 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignature.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
@@ -322,11 +322,30 @@
return this != GenericSignature.NO_FIELD_TYPE_SIGNATURE;
}
+ public boolean hasNoSignature() {
+ return !hasSignature();
+ }
+
public abstract FieldTypeSignature asArgument(WildcardIndicator indicator);
public boolean isStar() {
return false;
}
+
+ public String toRenamedString(NamingLens namingLens, Predicate<DexType> isTypeMissing) {
+ if (hasNoSignature()) {
+ return null;
+ }
+ GenericSignaturePrinter genericSignaturePrinter =
+ new GenericSignaturePrinter(namingLens, isTypeMissing);
+ genericSignaturePrinter.visitTypeSignature(this);
+ return genericSignaturePrinter.toString();
+ }
+
+ @Override
+ public String toString() {
+ return toRenamedString(NamingLens.getIdentityLens(), alwaysTrue());
+ }
}
static final class StarFieldTypeSignature extends FieldTypeSignature {
@@ -650,6 +669,9 @@
Origin origin,
DexItemFactory factory,
Reporter reporter) {
+ if (signature == null || signature.isEmpty()) {
+ return NO_FIELD_TYPE_SIGNATURE;
+ }
Parser parser = new Parser(factory);
try {
return parser.parseFieldTypeSignature(signature);
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java b/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java
index 1b23c85..e75ee78 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java
@@ -33,6 +33,11 @@
}
@Override
+ public void visitFieldTypeSignature(FieldTypeSignature fieldSignature) {
+ printFieldTypeSignature(fieldSignature, false);
+ }
+
+ @Override
public void visitFormalTypeParameters(List<FormalTypeParameter> formalTypeParameters) {
if (formalTypeParameters.isEmpty()) {
return;
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
index ddabc37..4cce25e 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
@@ -41,6 +41,13 @@
return new ClassSignatureRewriter().run(classSignature);
}
+ public FieldTypeSignature rewrite(FieldTypeSignature fieldTypeSignature) {
+ if (fieldTypeSignature.hasNoSignature() || appView.graphLens().isIdentityLens()) {
+ return fieldTypeSignature;
+ }
+ return new TypeSignatureRewriter().run(fieldTypeSignature);
+ }
+
private class ClassSignatureRewriter implements GenericSignatureVisitor {
private final List<FormalTypeParameter> rewrittenTypeParameters = new ArrayList<>();
@@ -228,10 +235,10 @@
}
private DexType getTarget(DexType type) {
- if (appInfoWithLiveness != null && appInfoWithLiveness.wasPruned(type)) {
+ DexType rewrittenType = appView.graphLens().lookupType(type);
+ if (appInfoWithLiveness != null && appInfoWithLiveness.wasPruned(rewrittenType)) {
return null;
}
- DexType rewrittenType = appView.graphLens().lookupType(type);
if (isSuperClassOrInterface && context.type == rewrittenType) {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureVisitor.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureVisitor.java
index 22ddca7..6d9385e 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureVisitor.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureVisitor.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
import com.android.tools.r8.graph.GenericSignature.ReturnType;
import com.android.tools.r8.graph.GenericSignature.TypeSignature;
-import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import java.util.List;
@@ -25,7 +24,7 @@
throw new Unreachable("Implement if visited");
}
- default void visitFieldSignature(FieldSignature fieldSignature) {
+ default void visitFieldTypeSignature(FieldTypeSignature fieldSignature) {
throw new Unreachable("Implement if visited");
}
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 4382eaa..ccaa837 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -33,6 +33,7 @@
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.DexValue.DexValueType;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
import com.android.tools.r8.jar.CfApplicationWriter;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
@@ -559,6 +560,7 @@
private final String name;
private final String desc;
private final Object value;
+ private FieldTypeSignature fieldSignature;
private List<DexAnnotation> annotations = null;
public CreateFieldVisitor(CreateDexClassVisitor parent,
@@ -569,10 +571,13 @@
this.name = name;
this.desc = desc;
this.value = value;
- if (signature != null && !signature.isEmpty()) {
- addAnnotation(
- DexAnnotation.createSignatureAnnotation(signature, parent.application.getFactory()));
- }
+ this.fieldSignature =
+ GenericSignature.parseFieldTypeSignature(
+ name,
+ signature,
+ parent.origin,
+ parent.application.getFactory(),
+ parent.application.options.reporter);
}
@Override
@@ -598,7 +603,12 @@
DexValue staticValue = flags.isStatic() ? getStaticValue(value, dexField.type) : null;
DexEncodedField field =
new DexEncodedField(
- dexField, flags, annotationSet, staticValue, AsmUtils.isDeprecated(access));
+ dexField,
+ flags,
+ fieldSignature,
+ annotationSet,
+ staticValue,
+ AsmUtils.isDeprecated(access));
if (flags.isStatic()) {
parent.staticFields.add(field);
} else {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index 73d1e68..4a4936b 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.horizontalclassmerging;
+import static com.android.tools.r8.graph.GenericSignature.NO_FIELD_TYPE_SIGNATURE;
+
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotationSet;
@@ -151,6 +153,7 @@
classIdField,
FieldAccessFlags.fromSharedAccessFlags(
Constants.ACC_PUBLIC + Constants.ACC_FINAL + Constants.ACC_SYNTHETIC),
+ NO_FIELD_TYPE_SIGNATURE,
DexAnnotationSet.empty(),
null);
target.appendInstanceField(encodedField);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
index b912cf7..01058e2 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
import java.util.Collection;
import java.util.Map;
+import java.util.Set;
public class HorizontallyMergedClasses implements MergedClasses {
private final BidirectionalManyToOneMap<DexType, DexType> mergedClasses;
@@ -24,6 +25,10 @@
return mergedClasses.getOrDefault(type, type);
}
+ public Set<DexType> getSources() {
+ return mergedClasses.keySet();
+ }
+
public boolean hasBeenMergedIntoDifferentType(DexType type) {
return mergedClasses.hasKey(type);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
index e33137c..7f12cc9 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -32,6 +32,7 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
@@ -384,10 +385,26 @@
if (fields == null) {
return;
}
+ Set<DexField> existingFields = Sets.newIdentityHashSet();
+
for (int i = 0; i < fields.size(); i++) {
DexEncodedField encodedField = fields.get(i);
DexField field = encodedField.field;
DexField newField = fixupFieldReference(field);
+
+ // Rename the field if it already exists.
+ if (!existingFields.add(newField)) {
+ DexField template = newField;
+ newField =
+ dexItemFactory.createFreshMember(
+ tryName ->
+ Optional.of(template.withName(tryName, dexItemFactory))
+ .filter(tryMethod -> !existingFields.contains(tryMethod)),
+ newField.name.toSourceString());
+ boolean added = existingFields.add(newField);
+ assert added;
+ }
+
if (newField != encodedField.field) {
lensBuilder.mapField(field, newField);
setter.setField(i, encodedField.toTypeSubstitutedField(newField));
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index b695024..f475e89 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -1135,9 +1135,13 @@
return false;
}
boolean didDesugar = false;
+ Supplier<AppInfoWithClassHierarchy> lazyAppInfo =
+ Suppliers.memoize(() -> appView.appInfoForDesugaring());
if (lambdaRewriter != null) {
- AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
- didDesugar |= lambdaRewriter.desugarLambdas(method, appInfo) > 0;
+ didDesugar |= lambdaRewriter.desugarLambdas(method, lazyAppInfo.get()) > 0;
+ }
+ if (backportedMethodRewriter != null) {
+ didDesugar |= backportedMethodRewriter.desugar(method, lazyAppInfo.get());
}
return didDesugar;
}
@@ -1423,12 +1427,6 @@
timing.end();
}
- if (backportedMethodRewriter != null) {
- timing.begin("Rewrite backport methods");
- backportedMethodRewriter.desugar(code);
- timing.end();
- }
-
timing.begin("Desugar string concat");
stringConcatRewriter.desugarStringConcats(method.method, code);
timing.end();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
index b283f96..d7b3d0a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
@@ -1,129 +1,47 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2020, 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;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexMethodHandle;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.conversion.IRBuilder;
-import java.util.ArrayList;
-import java.util.List;
+import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
// Source code representing synthesized accessor method.
-final class AccessorMethodSourceCode extends SynthesizedLambdaSourceCode {
- AccessorMethodSourceCode(LambdaClass lambda, Position callerPosition) {
- super(
- lambda, lambda.target.callTarget, callerPosition, null /* no receiver for static method */);
- // We should never need an accessor for interface methods since
- // they are supposed to be public.
- assert !descriptor().implHandle.type.isInvokeInterface();
- assert checkSignatures();
- }
+public class AccessorMethodSourceCode {
- private boolean checkSignatures() {
- DexMethodHandle implHandle = descriptor().implHandle;
- assert implHandle != null;
-
- DexType[] accessorParams = proto.parameters.values;
- DexMethod implMethod = implHandle.asMethod();
- DexProto implProto = implMethod.proto;
- DexType[] implParams = implProto.parameters.values;
-
- int index = 0;
- if (implHandle.type.isInvokeInstance() || implHandle.type.isInvokeDirect()) {
- assert accessorParams[index] == descriptor().getImplReceiverType();
- index++;
- }
-
- for (DexType implParam : implParams) {
- assert accessorParams[index] == implParam;
- index++;
- }
- assert index == accessorParams.length;
-
- assert delegatingToConstructor()
- ? this.proto.returnType == implMethod.holder
- : this.proto.returnType == implProto.returnType;
- return true;
- }
-
- // Are we delegating to a constructor?
- private boolean delegatingToConstructor() {
- return descriptor().implHandle.type.isInvokeConstructor();
- }
-
- private Invoke.Type inferInvokeType() {
- switch (descriptor().implHandle.type) {
+ public static CfCode build(LambdaClass lambda, DexMethod accessor) {
+ DexMethod target = lambda.descriptor.implHandle.asMethod();
+ ForwardMethodBuilder forwardMethodBuilder =
+ ForwardMethodBuilder.builder(lambda.appView.dexItemFactory()).setStaticSource(accessor);
+ switch (lambda.descriptor.implHandle.type) {
case INVOKE_INSTANCE:
- return Invoke.Type.VIRTUAL;
+ {
+ forwardMethodBuilder.setVirtualTarget(target, false);
+ break;
+ }
case INVOKE_STATIC:
- return Invoke.Type.STATIC;
+ {
+ forwardMethodBuilder.setStaticTarget(target, false);
+ break;
+ }
case INVOKE_DIRECT:
+ {
+ forwardMethodBuilder.setDirectTarget(target, false);
+ break;
+ }
case INVOKE_CONSTRUCTOR:
- return Invoke.Type.DIRECT;
+ {
+ forwardMethodBuilder.setConstructorTarget(target, lambda.appView.dexItemFactory());
+ break;
+ }
case INVOKE_INTERFACE:
throw new Unreachable("Accessor for an interface method?");
default:
throw new Unreachable();
}
- }
-
- @Override
- protected void prepareInstructions() {
- DexMethod implMethod = descriptor().implHandle.asMethod();
- DexType[] accessorParams = proto.parameters.values;
-
- // Prepare call arguments.
- List<ValueType> argValueTypes = new ArrayList<>();
- List<Integer> argRegisters = new ArrayList<>();
-
- // If we are delegating to a constructor, we need to create the instance
- // first. This instance will be the first argument to the call.
- if (delegatingToConstructor()) {
- int instance = nextRegister(ValueType.OBJECT);
- add(builder -> builder.addNewInstance(instance, implMethod.holder));
- argValueTypes.add(ValueType.OBJECT);
- argRegisters.add(instance);
- }
-
- for (int i = 0; i < accessorParams.length; i++) {
- DexType param = accessorParams[i];
- argValueTypes.add(ValueType.fromDexType(param));
- argRegisters.add(getParamRegister(i));
- }
-
- // Method call to the original impl-method.
- // Mirroring assert in constructor, we never need accessors to interfaces.
- assert !descriptor().implHandle.type.isInvokeInterface();
- add(
- builder ->
- builder.addInvoke(
- inferInvokeType(),
- implMethod,
- implMethod.proto,
- argValueTypes,
- argRegisters,
- false /* isInterface */));
-
- // Does the method have return value?
- if (proto.returnType == factory().voidType) {
- add(IRBuilder::addReturn);
- } else if (delegatingToConstructor()) {
- // Return newly created instance
- add(builder -> builder.addReturn(argRegisters.get(0)));
- } else {
- ValueType valueType = ValueType.fromDexType(proto.returnType);
- int tempValue = nextRegister(valueType);
- add(builder -> builder.addMoveResult(tempValue));
- add(builder -> builder.addReturn(tempValue));
- }
+ return forwardMethodBuilder.build();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 6dc372e..b45fe42 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -4,27 +4,25 @@
package com.android.tools.r8.ir.desugar;
-
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
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.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeStatic;
-import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.desugar.backports.BackportedMethods;
import com.android.tools.r8.ir.desugar.backports.BooleanMethodRewrites;
@@ -40,19 +38,19 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
import com.android.tools.r8.utils.Timing;
-import com.google.common.collect.Sets;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
+import java.util.ListIterator;
import java.util.Map;
import java.util.Queue;
-import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
+import org.objectweb.asm.Opcodes;
public final class BackportedMethodRewriter {
@@ -107,29 +105,34 @@
BackportedMethods.registerSynthesizedCodeReferences(options.itemFactory);
}
- public void desugar(IRCode code) {
+ public boolean desugar(ProgramMethod method, AppInfoWithClassHierarchy appInfo) {
if (!enabled) {
- return; // Nothing to do!
+ return false;
}
- Set<Value> affectedValues = Sets.newIdentityHashSet();
- InstructionListIterator iterator = code.instructionListIterator();
+ CfCode code = method.getDefinition().getCode().asCfCode();
+ ListIterator<CfInstruction> iterator = code.getInstructions().listIterator();
+ boolean replaced = false;
while (iterator.hasNext()) {
- Instruction instruction = iterator.next();
- if (!instruction.isInvokeMethod()) {
+ CfInvoke invoke = iterator.next().asInvoke();
+ if (invoke == null) {
continue;
}
-
- InvokeMethod invoke = instruction.asInvokeMethod();
- DexMethod invokedMethod = invoke.getInvokedMethod();
+ DexMethod invokedMethod = invoke.getMethod();
MethodProvider provider = getMethodProviderOrNull(invokedMethod);
if (provider != null) {
+ if (!replaced) {
+ // Create mutable instructions on first write.
+ ArrayList<CfInstruction> mutableInstructions = new ArrayList<>(code.getInstructions());
+ code.setInstructions(mutableInstructions);
+ iterator = mutableInstructions.listIterator(iterator.previousIndex());
+ iterator.next();
+ }
provider.rewriteInvoke(
- invoke, iterator, code, appView, affectedValues, synthesizedMethods::add);
+ invoke, iterator, method.getHolder(), appInfo, synthesizedMethods::add);
+ replaced = true;
}
}
- if (!affectedValues.isEmpty()) {
- new TypeAnalysis(appView).narrowing(affectedValues);
- }
+ return replaced;
}
public void processSynthesizedClasses(IRConverter converter, ExecutorService executor)
@@ -244,7 +247,7 @@
name = factory.createString("compare");
proto = factory.createProto(factory.intType, factory.longType, factory.longType);
method = factory.createMethod(type, proto, name);
- addProvider(new InvokeRewriter(method, LongMethodRewrites::rewriteCompare));
+ addProvider(new InvokeRewriter(method, LongMethodRewrites.rewriteCompare()));
// Boolean
type = factory.boxedBooleanType;
@@ -289,7 +292,7 @@
name = factory.createString("hash");
proto = factory.createProto(factory.intType, factory.objectArrayType);
method = factory.createMethod(type, proto, name);
- addProvider(new InvokeRewriter(method, ObjectsMethodRewrites::rewriteToArraysHashCode));
+ addProvider(new InvokeRewriter(method, ObjectsMethodRewrites.rewriteToArraysHashCode()));
// int Objects.hashCode(Object o)
name = factory.createString("hashCode");
@@ -299,7 +302,7 @@
// T Objects.requireNonNull(T obj)
method = factory.objectsMethods.requireNonNull;
- addProvider(new InvokeRewriter(method, ObjectsMethodRewrites::rewriteRequireNonNull));
+ addProvider(new InvokeRewriter(method, ObjectsMethodRewrites.rewriteRequireNonNull()));
// T Objects.requireNonNull(T obj, String message)
name = factory.createString("requireNonNull");
@@ -356,7 +359,7 @@
DexString name = factory.createString("hashCode");
DexProto proto = factory.createProto(factory.intType, factory.byteType);
DexMethod method = factory.createMethod(type, proto, name);
- addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteAsIdentity));
+ addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteAsIdentity()));
// Short
type = factory.boxedShortType;
@@ -364,7 +367,7 @@
name = factory.createString("hashCode");
proto = factory.createProto(factory.intType, factory.shortType);
method = factory.createMethod(type, proto, name);
- addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteAsIdentity));
+ addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteAsIdentity()));
// Integer
type = factory.boxedIntType;
@@ -373,25 +376,25 @@
name = factory.createString("hashCode");
proto = factory.createProto(factory.intType, factory.intType);
method = factory.createMethod(type, proto, name);
- addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteAsIdentity));
+ addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteAsIdentity()));
// int Integer.max(int a, int b)
name = factory.createString("max");
proto = factory.createProto(factory.intType, factory.intType, factory.intType);
method = factory.createMethod(type, proto, name);
- addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToInvokeMath));
+ addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath()));
// int Integer.min(int a, int b)
name = factory.createString("min");
proto = factory.createProto(factory.intType, factory.intType, factory.intType);
method = factory.createMethod(type, proto, name);
- addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToInvokeMath));
+ addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath()));
// int Integer.sum(int a, int b)
name = factory.createString("sum");
proto = factory.createProto(factory.intType, factory.intType, factory.intType);
method = factory.createMethod(type, proto, name);
- addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToAddInstruction));
+ addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToAddInstruction()));
// Double
type = factory.boxedDoubleType;
@@ -406,19 +409,19 @@
name = factory.createString("max");
proto = factory.createProto(factory.doubleType, factory.doubleType, factory.doubleType);
method = factory.createMethod(type, proto, name);
- addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToInvokeMath));
+ addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath()));
// double Double.min(double a, double b)
name = factory.createString("min");
proto = factory.createProto(factory.doubleType, factory.doubleType, factory.doubleType);
method = factory.createMethod(type, proto, name);
- addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToInvokeMath));
+ addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath()));
// double Double.sum(double a, double b)
name = factory.createString("sum");
proto = factory.createProto(factory.doubleType, factory.doubleType, factory.doubleType);
method = factory.createMethod(type, proto, name);
- addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToAddInstruction));
+ addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToAddInstruction()));
// boolean Double.isFinite(double a)
name = factory.createString("isFinite");
@@ -433,25 +436,25 @@
name = factory.createString("hashCode");
proto = factory.createProto(factory.intType, factory.floatType);
method = factory.createMethod(type, proto, name);
- addProvider(new InvokeRewriter(method, FloatMethodRewrites::rewriteHashCode));
+ addProvider(new InvokeRewriter(method, FloatMethodRewrites.rewriteHashCode()));
// float Float.max(float a, float b)
name = factory.createString("max");
proto = factory.createProto(factory.floatType, factory.floatType, factory.floatType);
method = factory.createMethod(type, proto, name);
- addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToInvokeMath));
+ addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath()));
// float Float.min(float a, float b)
name = factory.createString("min");
proto = factory.createProto(factory.floatType, factory.floatType, factory.floatType);
method = factory.createMethod(type, proto, name);
- addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToInvokeMath));
+ addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath()));
// float Float.sum(float a, float b)
name = factory.createString("sum");
proto = factory.createProto(factory.floatType, factory.floatType, factory.floatType);
method = factory.createMethod(type, proto, name);
- addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToAddInstruction));
+ addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToAddInstruction()));
// boolean Float.isFinite(float a)
name = factory.createString("isFinite");
@@ -472,19 +475,19 @@
name = factory.createString("logicalAnd");
proto = factory.createProto(factory.booleanType, factory.booleanType, factory.booleanType);
method = factory.createMethod(type, proto, name);
- addProvider(new InvokeRewriter(method, BooleanMethodRewrites::rewriteLogicalAnd));
+ addProvider(new InvokeRewriter(method, BooleanMethodRewrites.rewriteLogicalAnd()));
// boolean Boolean.logicalOr(boolean a, boolean b)
name = factory.createString("logicalOr");
proto = factory.createProto(factory.booleanType, factory.booleanType, factory.booleanType);
method = factory.createMethod(type, proto, name);
- addProvider(new InvokeRewriter(method, BooleanMethodRewrites::rewriteLogicalOr));
+ addProvider(new InvokeRewriter(method, BooleanMethodRewrites.rewriteLogicalOr()));
// boolean Boolean.logicalXor(boolean a, boolean b)
name = factory.createString("logicalXor");
proto = factory.createProto(factory.booleanType, factory.booleanType, factory.booleanType);
method = factory.createMethod(type, proto, name);
- addProvider(new InvokeRewriter(method, BooleanMethodRewrites::rewriteLogicalXor));
+ addProvider(new InvokeRewriter(method, BooleanMethodRewrites.rewriteLogicalXor()));
// Long
type = factory.boxedLongType;
@@ -499,19 +502,19 @@
name = factory.createString("max");
proto = factory.createProto(factory.longType, factory.longType, factory.longType);
method = factory.createMethod(type, proto, name);
- addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToInvokeMath));
+ addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath()));
// long Long.min(long a, long b)
name = factory.createString("min");
proto = factory.createProto(factory.longType, factory.longType, factory.longType);
method = factory.createMethod(type, proto, name);
- addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToInvokeMath));
+ addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToInvokeMath()));
// long Long.sum(long a, long b)
name = factory.createString("sum");
proto = factory.createProto(factory.longType, factory.longType, factory.longType);
method = factory.createMethod(type, proto, name);
- addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteToAddInstruction));
+ addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteToAddInstruction()));
// Character
type = factory.boxedCharType;
@@ -520,7 +523,7 @@
name = factory.createString("hashCode");
proto = factory.createProto(factory.intType, factory.charType);
method = factory.createMethod(type, proto, name);
- addProvider(new InvokeRewriter(method, NumericMethodRewrites::rewriteAsIdentity));
+ addProvider(new InvokeRewriter(method, NumericMethodRewrites.rewriteAsIdentity()));
// Objects
type = factory.objectsType;
@@ -912,7 +915,7 @@
method = factory.createMethod(type, proto, name);
addProvider(
i == 0
- ? new InvokeRewriter(method, CollectionMethodRewrites::rewriteListOfEmpty)
+ ? new InvokeRewriter(method, CollectionMethodRewrites.rewriteListOfEmpty())
: new MethodGenerator(
method,
(options, methodArg) ->
@@ -933,7 +936,7 @@
method = factory.createMethod(type, proto, name);
addProvider(
i == 0
- ? new InvokeRewriter(method, CollectionMethodRewrites::rewriteSetOfEmpty)
+ ? new InvokeRewriter(method, CollectionMethodRewrites.rewriteSetOfEmpty())
: new MethodGenerator(
method,
(options, methodArg) ->
@@ -953,7 +956,7 @@
method = factory.createMethod(type, proto, name);
addProvider(
i == 0
- ? new InvokeRewriter(method, CollectionMethodRewrites::rewriteMapOfEmpty)
+ ? new InvokeRewriter(method, CollectionMethodRewrites.rewriteMapOfEmpty())
: new MethodGenerator(
method,
(options, methodArg) ->
@@ -1220,10 +1223,10 @@
};
MethodInvokeRewriter[] rewriters =
new MethodInvokeRewriter[] {
- OptionalMethodRewrites::rewriteOrElseGet,
- OptionalMethodRewrites::rewriteDoubleOrElseGet,
- OptionalMethodRewrites::rewriteLongOrElseGet,
- OptionalMethodRewrites::rewriteIntOrElseGet,
+ OptionalMethodRewrites.rewriteOrElseGet(),
+ OptionalMethodRewrites.rewriteDoubleOrElseGet(),
+ OptionalMethodRewrites.rewriteLongOrElseGet(),
+ OptionalMethodRewrites.rewriteIntOrElseGet(),
};
DexString name = factory.createString("orElseThrow");
for (int i = 0; i < optionalTypes.length; i++) {
@@ -1290,11 +1293,10 @@
}
public abstract void rewriteInvoke(
- InvokeMethod invoke,
- InstructionListIterator iterator,
- IRCode code,
- AppView<?> appView,
- Set<Value> affectedValues,
+ CfInvoke invoke,
+ ListIterator<CfInstruction> iterator,
+ DexProgramClass context,
+ AppInfoWithClassHierarchy appInfo,
Consumer<ProgramMethod> registerSynthesizedMethod);
}
@@ -1309,14 +1311,12 @@
@Override
public void rewriteInvoke(
- InvokeMethod invoke,
- InstructionListIterator iterator,
- IRCode code,
- AppView<?> appView,
- Set<Value> affectedValues,
+ CfInvoke invoke,
+ ListIterator<CfInstruction> iterator,
+ DexProgramClass context,
+ AppInfoWithClassHierarchy appInfo,
Consumer<ProgramMethod> registerSynthesizedMethod) {
- rewriter.rewrite(invoke, iterator, appView.dexItemFactory(), affectedValues);
- assert code.isConsistentSSA();
+ rewriter.rewrite(invoke, iterator, appInfo.dexItemFactory());
}
}
@@ -1337,34 +1337,33 @@
@Override
public void rewriteInvoke(
- InvokeMethod invoke,
- InstructionListIterator iterator,
- IRCode code,
- AppView<?> appView,
- Set<Value> affectedValues,
+ CfInvoke invoke,
+ ListIterator<CfInstruction> iterator,
+ DexProgramClass context,
+ AppInfoWithClassHierarchy appInfo,
Consumer<ProgramMethod> registerSynthesizedMethod) {
- ProgramMethod method =
- appView
- .getSyntheticItems()
- .createMethod(
- code.context().getHolder(),
- appView.dexItemFactory(),
- builder ->
- builder
- .setProto(getProto(appView.dexItemFactory()))
- .setAccessFlags(
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC
- | Constants.ACC_STATIC
- | Constants.ACC_SYNTHETIC,
- false))
- .setCode(
- methodSig -> generateTemplateMethod(appView.options(), methodSig)));
-
- iterator.replaceCurrentInstruction(
- new InvokeStatic(method.getReference(), invoke.outValue(), invoke.inValues()));
-
+ ProgramMethod method = getSyntheticMethod(context, appInfo);
registerSynthesizedMethod.accept(method);
+ iterator.remove();
+ iterator.add(new CfInvoke(Opcodes.INVOKESTATIC, method.getReference(), false));
+ }
+
+ private ProgramMethod getSyntheticMethod(
+ DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
+ return appInfo
+ .getSyntheticItems()
+ .createMethod(
+ context,
+ appInfo.dexItemFactory(),
+ builder ->
+ builder
+ .setProto(getProto(appInfo.dexItemFactory()))
+ .setAccessFlags(
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC,
+ false))
+ .setCode(
+ methodSig -> generateTemplateMethod(appInfo.app().options, methodSig)));
}
public DexProto getProto(DexItemFactory itemFactory) {
@@ -1397,15 +1396,30 @@
private interface TemplateMethodFactory {
- Code create(InternalOptions options, DexMethod method);
+ CfCode create(InternalOptions options, DexMethod method);
}
- private interface MethodInvokeRewriter {
+ public interface MethodInvokeRewriter {
- void rewrite(
- InvokeMethod invoke,
- InstructionListIterator iterator,
- DexItemFactory factory,
- Set<Value> affectedValues);
+ CfInstruction rewriteSingle(CfInvoke invoke, DexItemFactory factory);
+
+ // Convenience wrapper since most rewrites are to a single instruction.
+ default void rewrite(
+ CfInvoke invoke, ListIterator<CfInstruction> iterator, DexItemFactory factory) {
+ iterator.remove();
+ iterator.add(rewriteSingle(invoke, factory));
+ }
+ }
+
+ public abstract static class FullMethodInvokeRewriter implements MethodInvokeRewriter {
+
+ @Override
+ public final CfInstruction rewriteSingle(CfInvoke invoke, DexItemFactory factory) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public abstract void rewrite(
+ CfInvoke invoke, ListIterator<CfInstruction> iterator, DexItemFactory factory);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
index 4d5c7be..01fb238 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
@@ -22,10 +22,8 @@
import com.android.tools.r8.graph.DexValue.DexValueType;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
-import com.android.tools.r8.ir.synthetic.SynthesizedCode;
+import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
import com.google.common.base.Predicates;
import java.util.HashSet;
import java.util.LinkedList;
@@ -111,12 +109,13 @@
DexProgramClass clazz,
List<DexEncodedMethod> methodsWithCovariantReturnTypeAnnotation,
List<DexEncodedMethod> covariantReturnTypeMethods) {
- for (DexEncodedMethod method : clazz.virtualMethods()) {
- if (methodHasCovariantReturnTypeAnnotation(method)) {
- methodsWithCovariantReturnTypeAnnotation.add(method);
- buildCovariantReturnTypeMethodsForMethod(clazz, method, covariantReturnTypeMethods);
- }
- }
+ clazz.forEachProgramVirtualMethod(
+ method -> {
+ if (methodHasCovariantReturnTypeAnnotation(method.getDefinition())) {
+ methodsWithCovariantReturnTypeAnnotation.add(method.getDefinition());
+ buildCovariantReturnTypeMethodsForMethod(method, covariantReturnTypeMethods);
+ }
+ });
}
private boolean methodHasCovariantReturnTypeAnnotation(DexEncodedMethod method) {
@@ -132,13 +131,11 @@
// variantReturnTypes annotations on the given method. Adds the newly constructed, synthetic
// methods to the list covariantReturnTypeMethods.
private void buildCovariantReturnTypeMethodsForMethod(
- DexProgramClass clazz,
- DexEncodedMethod method,
- List<DexEncodedMethod> covariantReturnTypeMethods) {
- assert methodHasCovariantReturnTypeAnnotation(method);
- for (DexType covariantReturnType : getCovariantReturnTypes(clazz, method)) {
+ ProgramMethod method, List<DexEncodedMethod> covariantReturnTypeMethods) {
+ assert methodHasCovariantReturnTypeAnnotation(method.getDefinition());
+ for (DexType covariantReturnType : getCovariantReturnTypes(method)) {
DexEncodedMethod covariantReturnTypeMethod =
- buildCovariantReturnTypeMethod(clazz, method, covariantReturnType);
+ buildCovariantReturnTypeMethod(method, covariantReturnType);
covariantReturnTypeMethods.add(covariantReturnTypeMethod);
}
}
@@ -149,32 +146,35 @@
//
// Note: any "synchronized" or "strictfp" modifier could be dropped safely.
private DexEncodedMethod buildCovariantReturnTypeMethod(
- DexProgramClass clazz, DexEncodedMethod method, DexType covariantReturnType) {
+ ProgramMethod method, DexType covariantReturnType) {
+ DexProgramClass methodHolder = method.getHolder();
+ DexMethod methodReference = method.getReference();
+ DexEncodedMethod methodDefinition = method.getDefinition();
DexProto newProto =
factory.createProto(
- covariantReturnType, method.method.proto.parameters, method.method.proto.shorty);
- MethodAccessFlags newAccessFlags = method.accessFlags.copy();
+ covariantReturnType, methodReference.proto.parameters, methodReference.proto.shorty);
+ MethodAccessFlags newAccessFlags = methodDefinition.accessFlags.copy();
newAccessFlags.setBridge();
newAccessFlags.setSynthetic();
- DexMethod newMethod = factory.createMethod(method.holder(), newProto, method.method.name);
- ForwardMethodSourceCode.Builder forwardSourceCodeBuilder =
- ForwardMethodSourceCode.builder(newMethod);
- forwardSourceCodeBuilder
- .setReceiver(clazz.type)
- .setTargetReceiver(method.holder())
- .setTarget(method.method)
- .setInvokeType(Invoke.Type.VIRTUAL)
- .setCastResult();
+ DexMethod newMethod =
+ factory.createMethod(methodHolder.getType(), newProto, methodReference.getName());
+ ForwardMethodBuilder forwardMethodBuilder =
+ ForwardMethodBuilder.builder(factory)
+ .setNonStaticSource(newMethod)
+ .setVirtualTarget(methodReference, methodHolder.isInterface())
+ .setCastResult();
DexEncodedMethod newVirtualMethod =
new DexEncodedMethod(
newMethod,
newAccessFlags,
- method.annotations().keepIf(x -> !isCovariantReturnTypeAnnotation(x.annotation)),
- method.parameterAnnotationsList.keepIf(Predicates.alwaysTrue()),
- new SynthesizedCode(forwardSourceCodeBuilder::build),
+ methodDefinition
+ .annotations()
+ .keepIf(x -> !isCovariantReturnTypeAnnotation(x.annotation)),
+ methodDefinition.parameterAnnotationsList.keepIf(Predicates.alwaysTrue()),
+ forwardMethodBuilder.build(),
true);
- // Optimize to generate DexCode instead of SynthesizedCode.
- ProgramMethod programMethod = new ProgramMethod(clazz, newVirtualMethod);
+ // Optimize to generate DexCode instead of CfCode.
+ ProgramMethod programMethod = new ProgramMethod(methodHolder, newVirtualMethod);
converter.optimizeSynthesizedMethod(programMethod);
return newVirtualMethod;
}
@@ -187,12 +187,15 @@
// @Override
// public Foo foo() { ... return new SubOfSubOfFoo(); }
// then this method returns the set { SubOfFoo, SubOfSubOfFoo }.
- private Set<DexType> getCovariantReturnTypes(DexClass clazz, DexEncodedMethod method) {
+ private Set<DexType> getCovariantReturnTypes(ProgramMethod method) {
Set<DexType> covariantReturnTypes = new HashSet<>();
- for (DexAnnotation annotation : method.annotations().annotations) {
+ for (DexAnnotation annotation : method.getDefinition().annotations().annotations) {
if (isCovariantReturnTypeAnnotation(annotation.annotation)) {
getCovariantReturnTypesFromAnnotation(
- clazz, method, annotation.annotation, covariantReturnTypes);
+ method.getHolder(),
+ method.getDefinition(),
+ annotation.annotation,
+ covariantReturnTypes);
}
}
return covariantReturnTypes;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
index f2ea71d..88fc45a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.desugar;
+import static com.android.tools.r8.graph.GenericSignature.NO_FIELD_TYPE_SIGNATURE;
+
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
@@ -427,7 +429,8 @@
// Field is package private to be accessible from convert methods without a getter.
FieldAccessFlags fieldAccessFlags =
FieldAccessFlags.fromCfAccessFlags(Constants.ACC_FINAL | Constants.ACC_SYNTHETIC);
- return new DexEncodedField(field, fieldAccessFlags, DexAnnotationSet.empty(), null);
+ return new DexEncodedField(
+ field, fieldAccessFlags, NO_FIELD_TYPE_SIGNATURE, DexAnnotationSet.empty(), null);
}
private DexEncodedMethod synthesizeConstructor(DexField field) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index 67d389f..069f4b3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -32,8 +32,7 @@
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
-import com.android.tools.r8.ir.synthetic.SynthesizedCode;
+import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.utils.Pair;
import com.google.common.collect.BiMap;
@@ -260,15 +259,13 @@
DexMethod origMethod = direct.method;
DexMethod newMethod = rewriter.staticAsMethodOfDispatchClass(origMethod);
- ForwardMethodSourceCode.Builder forwardSourceCodeBuilder =
- ForwardMethodSourceCode.builder(newMethod);
// Create a forwarding method to the library static interface method. The method is added
// to the dispatch class, however, the targeted method is still on the interface, so the
// interface bit should be set to true.
- forwardSourceCodeBuilder
- .setTarget(origMethod)
- .setInvokeType(Type.STATIC)
- .setIsInterface(true);
+ ForwardMethodBuilder forwardMethodBuilder =
+ ForwardMethodBuilder.builder(appView.dexItemFactory())
+ .setStaticSource(newMethod)
+ .setStaticTarget(origMethod, true);
DexEncodedMethod newEncodedMethod =
new DexEncodedMethod(
newMethod,
@@ -276,7 +273,7 @@
Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC, false),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- new SynthesizedCode(forwardSourceCodeBuilder::build),
+ forwardMethodBuilder.build(),
true);
newEncodedMethod.getMutableOptimizationInfo().markNeverInline();
dispatchMethods.add(newEncodedMethod);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaAccessorMethodWithSynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaAccessorMethodWithSynthesizedCode.java
deleted file mode 100644
index a733540..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaAccessorMethodWithSynthesizedCode.java
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) 2020, 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;
-
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexMethodHandle;
-import com.android.tools.r8.graph.UseRegistry;
-import java.util.function.Consumer;
-
-public class LambdaAccessorMethodWithSynthesizedCode extends LambdaSynthesizedCode {
-
- public LambdaAccessorMethodWithSynthesizedCode(LambdaClass lambda) {
- super(lambda);
- }
-
- @Override
- public SourceCodeProvider getSourceCodeProvider() {
- return callerPosition -> new AccessorMethodSourceCode(lambda, callerPosition);
- }
-
- @Override
- public Consumer<UseRegistry> getRegistryCallback() {
- return registry -> {
- DexMethodHandle handle = lambda.descriptor.implHandle;
- DexMethod target = handle.asMethod();
- switch (handle.type) {
- case INVOKE_STATIC:
- registry.registerInvokeStatic(target);
- break;
- case INVOKE_INSTANCE:
- registry.registerInvokeVirtual(target);
- break;
- case INVOKE_CONSTRUCTOR:
- case INVOKE_DIRECT:
- registry.registerInvokeDirect(target);
- break;
- default:
- throw new Unreachable("Unexpected handle type: " + handle.type);
- }
- };
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSourceCode.java
index f0eb914..ddcdc6e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSourceCode.java
@@ -4,71 +4,21 @@
package com.android.tools.r8.ir.desugar;
+import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.conversion.IRBuilder;
-import java.util.ArrayList;
-import java.util.List;
+import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
// Source code representing synthesized lambda bridge method.
-final class LambdaBridgeMethodSourceCode extends SynthesizedLambdaSourceCode {
- private final DexMethod mainMethod;
+final class LambdaBridgeMethodSourceCode {
- LambdaBridgeMethodSourceCode(
- LambdaClass lambda, DexMethod mainMethod, DexMethod bridgeMethod, Position callerPosition) {
- super(lambda, bridgeMethod, callerPosition);
- this.mainMethod = mainMethod;
- }
-
- @Override
- protected void prepareInstructions() {
- DexType[] currentParams = proto.parameters.values;
- DexType[] enforcedParams = descriptor().enforcedProto.parameters.values;
-
- // Prepare call arguments.
- List<ValueType> argValueTypes = new ArrayList<>();
- List<Integer> argRegisters = new ArrayList<>();
-
- // Always add a receiver representing 'this' of the lambda class.
- argValueTypes.add(ValueType.OBJECT);
- argRegisters.add(getReceiverRegister());
-
- // Prepare arguments.
- for (int i = 0; i < currentParams.length; i++) {
- DexType expectedParamType = enforcedParams[i];
- argValueTypes.add(ValueType.fromDexType(expectedParamType));
- argRegisters.add(enforceParameterType(
- getParamRegister(i), currentParams[i], expectedParamType));
- }
-
- // Method call to the main functional interface method.
- add(
- builder ->
- builder.addInvoke(
- Invoke.Type.VIRTUAL,
- this.mainMethod,
- this.mainMethod.proto,
- argValueTypes,
- argRegisters,
- false /* isInterface */));
-
- // Does the method have return value?
- if (proto.returnType == factory().voidType) {
- add(IRBuilder::addReturn);
- } else {
- ValueType valueType = ValueType.fromDexType(proto.returnType);
- int tempValue = nextRegister(valueType);
- add(builder -> builder.addMoveResult(tempValue));
- // We lack precise sub-type information, but there should not be a need to cast to object.
- if (proto.returnType != mainMethod.proto.returnType
- && proto.returnType != factory().objectType) {
- add(builder -> builder.addCheckCast(tempValue, proto.returnType));
- }
- add(builder -> builder.addReturn(tempValue));
- }
+ public static CfCode build(
+ LambdaClass lambdaClass, DexMethod bridgeMethod, DexMethod mainMethod) {
+ return ForwardMethodBuilder.builder(lambdaClass.appView.dexItemFactory())
+ .setNonStaticSource(bridgeMethod)
+ .setVirtualTarget(mainMethod, false)
+ .setCastArguments(lambdaClass.appView.appInfoForDesugaring())
+ .setCastResult()
+ .build();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSynthesizedCode.java
deleted file mode 100644
index 6e109dd..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaBridgeMethodSynthesizedCode.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (c) 2019, 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;
-
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.UseRegistry;
-import java.util.function.Consumer;
-
-class LambdaBridgeMethodSynthesizedCode extends LambdaSynthesizedCode {
-
- private final DexMethod mainMethod;
- private final DexMethod bridgeMethod;
-
- LambdaBridgeMethodSynthesizedCode(
- LambdaClass lambda, DexMethod mainMethod, DexMethod bridgeMethod) {
- super(lambda);
- this.mainMethod = mainMethod;
- this.bridgeMethod = bridgeMethod;
- }
-
- @Override
- public SourceCodeProvider getSourceCodeProvider() {
- return callerPosition ->
- new LambdaBridgeMethodSourceCode(lambda, mainMethod, bridgeMethod, callerPosition);
- }
-
- @Override
- public Consumer<UseRegistry> getRegistryCallback() {
- return registry -> {
- registry.registerInvokeVirtual(mainMethod);
-
- DexType bridgeMethodReturnType = bridgeMethod.proto.returnType;
- if (!bridgeMethodReturnType.isVoidType()
- && bridgeMethodReturnType != mainMethod.proto.returnType
- && bridgeMethodReturnType != dexItemFactory().objectType) {
- registry.registerCheckCast(bridgeMethodReturnType);
- }
- };
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index cc6fec7..6f4cad3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.desugar;
+import static com.android.tools.r8.graph.GenericSignature.NO_FIELD_TYPE_SIGNATURE;
+
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
@@ -234,7 +236,7 @@
Constants.ACC_PUBLIC | Constants.ACC_FINAL, false),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- new LambdaMainMethodSynthesizedCode(this, mainMethod),
+ LambdaMainMethodSourceCode.build(this, mainMethod),
true);
// Synthesize bridge methods.
@@ -252,7 +254,7 @@
false),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- new LambdaBridgeMethodSynthesizedCode(this, mainMethod, bridgeMethod),
+ LambdaBridgeMethodSourceCode.build(this, bridgeMethod, mainMethod),
true);
}
return methods;
@@ -273,7 +275,7 @@
true),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- new LambdaConstructorSynthesizedCode(this),
+ LambdaConstructorSourceCode.build(this),
true);
// Class constructor for stateless lambda classes.
@@ -285,7 +287,7 @@
Constants.ACC_SYNTHETIC | Constants.ACC_STATIC, true),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- new LambdaClassConstructorSynthesizedCode(this),
+ LambdaClassConstructorSourceCode.build(this),
true);
}
return methods;
@@ -300,8 +302,13 @@
FieldAccessFlags accessFlags =
FieldAccessFlags.fromSharedAccessFlags(
Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC);
- fields[i] = new DexEncodedField(
- getCaptureField(i), accessFlags, DexAnnotationSet.empty(), null);
+ fields[i] =
+ new DexEncodedField(
+ getCaptureField(i),
+ accessFlags,
+ NO_FIELD_TYPE_SIGNATURE,
+ DexAnnotationSet.empty(),
+ null);
}
return fields;
}
@@ -323,6 +330,7 @@
| Constants.ACC_FINAL
| Constants.ACC_SYNTHETIC
| Constants.ACC_STATIC),
+ NO_FIELD_TYPE_SIGNATURE,
DexAnnotationSet.empty(),
DexValueNull.NULL);
return fields;
@@ -748,13 +756,14 @@
MethodAccessFlags.fromSharedAccessFlags(
Constants.ACC_SYNTHETIC | Constants.ACC_STATIC | Constants.ACC_PUBLIC,
false);
+
DexEncodedMethod accessorEncodedMethod =
new DexEncodedMethod(
callTarget,
accessorFlags,
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- new LambdaAccessorMethodWithSynthesizedCode(LambdaClass.this),
+ AccessorMethodSourceCode.build(LambdaClass.this, callTarget),
true);
// We may arrive here concurrently so we need must update the methods of the class atomically.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
index 6b85039..fd7a8b9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
@@ -4,40 +4,34 @@
package com.android.tools.r8.ir.desugar;
-import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.cf.code.CfFieldInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfNew;
+import com.android.tools.r8.cf.code.CfReturnVoid;
+import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
+import com.android.tools.r8.graph.CfCode;
import com.google.common.collect.ImmutableList;
+import org.objectweb.asm.Opcodes;
// Source code representing synthesized lambda class constructor.
// Used for stateless lambdas to instantiate singleton instance.
-final class LambdaClassConstructorSourceCode extends SynthesizedLambdaSourceCode {
+final class LambdaClassConstructorSourceCode {
- LambdaClassConstructorSourceCode(LambdaClass lambda, Position callerPosition) {
- super(lambda, lambda.classConstructor, callerPosition, null /* Class initializer is static */);
- assert lambda.lambdaField != null;
- }
-
- @Override
- protected void prepareInstructions() {
- // Create and initialize an instance.
- int instance = nextRegister(ValueType.OBJECT);
- add(builder -> builder.addNewInstance(instance, lambda.type));
- add(
- builder ->
- builder.addInvoke(
- Invoke.Type.DIRECT,
- lambda.constructor,
- lambda.constructor.proto,
- ImmutableList.of(ValueType.OBJECT),
- ImmutableList.of(instance),
- false /* isInterface */));
-
- // Assign to a field.
- add(builder -> builder.addStaticPut(instance, lambda.lambdaField));
-
- // Final return.
- add(IRBuilder::addReturn);
+ public static CfCode build(LambdaClass lambda) {
+ int maxStack = 2;
+ int maxLocals = 0;
+ return new CfCode(
+ lambda.type,
+ maxStack,
+ maxLocals,
+ ImmutableList.of(
+ new CfNew(lambda.type),
+ new CfStackInstruction(Opcode.Dup),
+ new CfInvoke(Opcodes.INVOKESPECIAL, lambda.constructor, false),
+ new CfFieldInstruction(Opcodes.PUTSTATIC, lambda.lambdaField, lambda.lambdaField),
+ new CfReturnVoid()),
+ ImmutableList.of(),
+ ImmutableList.of());
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSynthesizedCode.java
deleted file mode 100644
index 2dbac91..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSynthesizedCode.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2019, 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;
-
-import com.android.tools.r8.graph.UseRegistry;
-import java.util.function.Consumer;
-
-class LambdaClassConstructorSynthesizedCode extends LambdaSynthesizedCode {
-
- LambdaClassConstructorSynthesizedCode(LambdaClass lambda) {
- super(lambda);
- }
-
- @Override
- public SourceCodeProvider getSourceCodeProvider() {
- return callerPosition -> new LambdaClassConstructorSourceCode(lambda, callerPosition);
- }
-
- @Override
- public Consumer<UseRegistry> getRegistryCallback() {
- return registry -> {
- registry.registerNewInstance(lambda.type);
- registry.registerInvokeDirect(lambda.constructor);
- registry.registerStaticFieldWrite(lambda.lambdaField);
- };
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java
index a8161d4..65ead77 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSourceCode.java
@@ -4,70 +4,56 @@
package com.android.tools.r8.ir.desugar;
+import com.android.tools.r8.cf.code.CfFieldInstruction;
+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.CfReturnVoid;
+import com.android.tools.r8.cf.code.CfTryCatch;
+import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.conversion.IRBuilder;
-import java.util.Collections;
-
// Source code representing synthesized lambda constructor.
-final class LambdaConstructorSourceCode extends SynthesizedLambdaSourceCode {
+import com.android.tools.r8.ir.code.ValueType;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import org.objectweb.asm.Opcodes;
- LambdaConstructorSourceCode(LambdaClass lambda, Position callerPosition) {
- super(lambda, lambda.constructor, callerPosition);
- }
+final class LambdaConstructorSourceCode {
- @Override
- protected void prepareInstructions() {
+ public static CfCode build(LambdaClass lambda) {
+ int maxStack = 1;
+ ImmutableList<CfTryCatch> tryCatchRanges = ImmutableList.of();
+ ImmutableList<CfCode.LocalVariableInfo> localVariables = ImmutableList.of();
+ Builder<CfInstruction> instructions = ImmutableList.builder();
// Super constructor call (always java.lang.Object.<init>()).
- DexMethod objectInitMethod = factory().objectMembers.constructor;
- add(
- builder -> {
- assert builder.getReceiverValue() != null;
- builder.addInvoke(
- Invoke.Type.DIRECT,
- objectInitMethod,
- objectInitMethod.proto,
- Collections.singletonList(builder.getReceiverValue()),
- false /* isInterface */);
- });
-
+ instructions.add(new CfLoad(ValueType.OBJECT, 0));
+ instructions.add(
+ new CfInvoke(
+ Opcodes.INVOKESPECIAL,
+ lambda.appView.dexItemFactory().objectMembers.constructor,
+ false));
// Assign capture fields.
- DexType[] capturedTypes = captures();
- int capturedValues = capturedTypes.length;
- if (capturedValues > 0) {
- for (int i = 0; i < capturedValues; i++) {
- DexField field = lambda.getCaptureField(i);
- int idx = i;
- add(builder -> builder.addInstancePut(getParamRegister(idx), getReceiverRegister(), field));
- }
+ DexType[] capturedTypes = lambda.descriptor.captures.values;
+ int maxLocals = 1;
+ for (int i = 0; i < capturedTypes.length; i++) {
+ DexField field = lambda.getCaptureField(i);
+ assert field.type == capturedTypes[i];
+ ValueType type = ValueType.fromDexType(field.type);
+ instructions.add(new CfLoad(ValueType.OBJECT, 0));
+ instructions.add(new CfLoad(type, maxLocals));
+ instructions.add(new CfFieldInstruction(Opcodes.PUTFIELD, field, field));
+ maxLocals += type.requiredRegisters();
+ maxStack += type.requiredRegisters();
}
-
// Final return.
- add(IRBuilder::addReturn);
- }
-
- @Override
- public int hashCode() {
- // We want all zero-parameter constructor source code instances to
- // be treated as equal, since it only has one call to super constructor,
- // which is always java.lang.Object.<init>().
- return captures().length == 0
- ? System.identityHashCode(factory().objectMembers.constructor)
- : super.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof LambdaConstructorSourceCode)) {
- return false;
- }
- if (captures().length == 0) {
- // See comment in hashCode().
- return ((LambdaConstructorSourceCode) obj).captures().length == 0;
- }
- return super.equals(obj);
+ instructions.add(new CfReturnVoid());
+ return new CfCode(
+ lambda.constructor.holder,
+ maxStack,
+ maxLocals,
+ instructions.build(),
+ tryCatchRanges,
+ localVariables);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSynthesizedCode.java
deleted file mode 100644
index 1809d69..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaConstructorSynthesizedCode.java
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) 2019, 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;
-
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.UseRegistry;
-import java.util.function.Consumer;
-
-class LambdaConstructorSynthesizedCode extends LambdaSynthesizedCode {
-
- LambdaConstructorSynthesizedCode(LambdaClass lambda) {
- super(lambda);
- }
-
- @Override
- public SourceCodeProvider getSourceCodeProvider() {
- return callerPosition -> new LambdaConstructorSourceCode(lambda, callerPosition);
- }
-
- @Override
- public Consumer<UseRegistry> getRegistryCallback() {
- return registry -> {
- registry.registerInvokeDirect(dexItemFactory().objectMembers.constructor);
- DexType[] capturedTypes = captures();
- for (int i = 0; i < capturedTypes.length; i++) {
- registry.registerInstanceFieldWrite(lambda.getCaptureField(i));
- }
- };
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
index ac55e68..30ea01c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
@@ -4,7 +4,21 @@
package com.android.tools.r8.ir.desugar;
+import com.android.tools.r8.cf.code.CfCheckCast;
+import com.android.tools.r8.cf.code.CfFieldInstruction;
+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.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.CfStackInstruction.Opcode;
+import com.android.tools.r8.cf.code.CfThrow;
+import com.android.tools.r8.cf.code.CfTryCatch;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
@@ -12,27 +26,25 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.NumericType;
-import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.conversion.IRBuilder;
import com.android.tools.r8.ir.desugar.LambdaClass.InvalidLambdaImplTarget;
-import com.android.tools.r8.ir.synthetic.ExceptionThrowingSourceCode;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.Lists;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
+import org.objectweb.asm.Opcodes;
// Source code representing synthesized lambda main method
-final class LambdaMainMethodSourceCode extends SynthesizedLambdaSourceCode {
+final class LambdaMainMethodSourceCode {
- LambdaMainMethodSourceCode(LambdaClass lambda, DexMethod mainMethod, Position callerPosition) {
- super(lambda, mainMethod, callerPosition);
- }
-
- private boolean checkSignatures(
- DexType[] captures, DexType[] enforcedParams, DexType enforcedReturnType,
- List<DexType> implReceiverAndArgs, DexType implReturnType) {
+ private static boolean checkSignatures(
+ DexType[] captures,
+ DexType[] enforcedParams,
+ DexType enforcedReturnType,
+ List<DexType> implReceiverAndArgs,
+ DexType implReturnType,
+ DexItemFactory factory) {
List<DexType> capturesAndParams = new ArrayList<>();
capturesAndParams.addAll(Lists.newArrayList(captures));
capturesAndParams.addAll(Lists.newArrayList(enforcedParams));
@@ -43,23 +55,19 @@
}
for (int i = 0; i < size; i++) {
- if (!isSameOrAdaptableTo(capturesAndParams.get(i), implReceiverAndArgs.get(i))) {
+ if (!isSameOrAdaptableTo(capturesAndParams.get(i), implReceiverAndArgs.get(i), factory)) {
assert false;
}
}
if (!enforcedReturnType.isVoidType()
- && !isSameOrAdaptableTo(implReturnType, enforcedReturnType)) {
+ && !isSameOrAdaptableTo(implReturnType, enforcedReturnType, factory)) {
assert false;
}
return true;
}
- private DexType getPrimitiveFromBoxed(DexType boxedPrimitive) {
- return factory().getPrimitiveFromBoxed(boxedPrimitive);
- }
-
- private DexType getBoxedForPrimitiveType(DexType primitive) {
+ private static DexType getBoxedForPrimitiveType(DexType primitive, DexItemFactory factory) {
switch (primitive.descriptor.content[0]) {
case 'Z': // byte
case 'B': // byte
@@ -69,19 +77,18 @@
case 'J': // long
case 'F': // float
case 'D': // double
- return factory().getBoxedForPrimitiveType(primitive);
+ return factory.getBoxedForPrimitiveType(primitive);
default:
throw new Unreachable("Invalid primitive type descriptor: " + primitive);
}
}
// Checks if the types are the same OR type `a` is adaptable to type `b`.
- private boolean isSameOrAdaptableTo(DexType a, DexType b) {
+ private static boolean isSameOrAdaptableTo(DexType a, DexType b, DexItemFactory factory) {
if (a == b) {
return true;
}
- DexItemFactory factory = factory();
if (a.isArrayType()) {
// Arrays are only adaptable to java.lang.Object or other arrays, note that we
// don't check element type inheritance in the second case since we assume the
@@ -100,7 +107,7 @@
}
// `a` is primitive and `b` is a supertype of the boxed type `a`.
- DexType boxedPrimitiveType = getBoxedForPrimitiveType(a);
+ DexType boxedPrimitiveType = getBoxedForPrimitiveType(a, factory);
if (b == boxedPrimitiveType ||
b == factory.objectType ||
b == factory.serializableType ||
@@ -120,7 +127,7 @@
}
// `a` is a boxed type for `a*` which can be
// widened to primitive type `b`.
- DexType unboxedA = getPrimitiveFromBoxed(a);
+ DexType unboxedA = factory.getPrimitiveFromBoxed(a);
return unboxedA != null &&
isSameOrAdaptableTo(unboxedA.descriptor.content[0], b.descriptor.content[0]);
}
@@ -135,7 +142,7 @@
// For two primitive types `a` is adjustable to `b` iff `a` is the same as `b`
// or can be converted to `b` via a primitive widening conversion.
- private boolean isSameOrAdaptableTo(byte from, byte to) {
+ private static boolean isSameOrAdaptableTo(byte from, byte to) {
if (from == to) {
return true;
}
@@ -159,33 +166,31 @@
}
}
- @Override
- protected void prepareInstructions() {
- DexType[] capturedTypes = captures();
- DexType[] erasedParams = descriptor().erasedProto.parameters.values;
- DexType erasedReturnType = descriptor().erasedProto.returnType;
- DexType[] enforcedParams = descriptor().enforcedProto.parameters.values;
- DexType enforcedReturnType = descriptor().enforcedProto.returnType;
-
+ public static CfCode build(LambdaClass lambda, DexMethod mainMethod) {
+ DexItemFactory factory = lambda.appView.dexItemFactory();
LambdaClass.Target target = lambda.target;
if (target instanceof InvalidLambdaImplTarget) {
- add(
- builder -> {
- InvalidLambdaImplTarget invalidTarget = (InvalidLambdaImplTarget) target;
- ExceptionThrowingSourceCode.build(builder, invalidTarget.exceptionType);
- });
- return;
+ InvalidLambdaImplTarget invalidTarget = (InvalidLambdaImplTarget) target;
+ DexType exceptionType = invalidTarget.exceptionType;
+ return buildThrowingCode(mainMethod, exceptionType, factory);
}
DexMethod methodToCall = target.callTarget;
+ DexType[] capturedTypes = lambda.descriptor.captures.values;
+ DexType[] erasedParams = lambda.descriptor.erasedProto.parameters.values;
+ DexType erasedReturnType = lambda.descriptor.erasedProto.returnType;
+ DexType[] enforcedParams = lambda.descriptor.enforcedProto.parameters.values;
+ DexType enforcedReturnType = lambda.descriptor.enforcedProto.returnType;
// Only constructor call should use direct invoke type since super
// and private methods require accessor methods.
boolean constructorTarget = target.invokeType == Invoke.Type.DIRECT;
- assert !constructorTarget || methodToCall.name == factory().constructorMethodName;
+ assert !constructorTarget || methodToCall.name == factory.constructorMethodName;
+ boolean targetWithReceiver =
+ target.invokeType == Invoke.Type.VIRTUAL || target.invokeType == Invoke.Type.INTERFACE;
List<DexType> implReceiverAndArgs = new ArrayList<>();
- if (target.invokeType == Invoke.Type.VIRTUAL || target.invokeType == Invoke.Type.INTERFACE) {
+ if (targetWithReceiver) {
implReceiverAndArgs.add(methodToCall.holder);
}
implReceiverAndArgs.addAll(Lists.newArrayList(methodToCall.proto.parameters.values));
@@ -195,87 +200,129 @@
|| target.invokeType == Invoke.Type.VIRTUAL
|| target.invokeType == Invoke.Type.DIRECT
|| target.invokeType == Invoke.Type.INTERFACE;
- assert checkSignatures(capturedTypes, enforcedParams,
- enforcedReturnType, implReceiverAndArgs,
- constructorTarget ? target.callTarget.holder : implReturnType);
+ assert checkSignatures(
+ capturedTypes,
+ enforcedParams,
+ enforcedReturnType,
+ implReceiverAndArgs,
+ constructorTarget ? target.callTarget.holder : implReturnType,
+ factory);
- // Prepare call arguments.
- List<ValueType> argValueTypes = new ArrayList<>();
- List<Integer> argRegisters = new ArrayList<>();
+ int maxStack = 0;
+ Builder<CfInstruction> instructions = ImmutableList.builder();
// If the target is a constructor, we need to create the instance first.
- // This instance will be the first argument to the call.
+ // This instance will be the first argument to the call and the dup will be on stack at return.
if (constructorTarget) {
- int instance = nextRegister(ValueType.OBJECT);
- add(builder -> builder.addNewInstance(instance, methodToCall.holder));
- argValueTypes.add(ValueType.OBJECT);
- argRegisters.add(instance);
+ instructions.add(new CfNew(methodToCall.holder));
+ instructions.add(new CfStackInstruction(Opcode.Dup));
+ maxStack += 2;
}
// Load captures if needed.
int capturedValues = capturedTypes.length;
for (int i = 0; i < capturedValues; i++) {
- ValueType valueType = ValueType.fromDexType(capturedTypes[i]);
- int register = nextRegister(valueType);
-
- argValueTypes.add(valueType);
- argRegisters.add(register);
-
- // Read field into tmp local.
DexField field = lambda.getCaptureField(i);
- add(builder -> builder.addInstanceGet(register, getReceiverRegister(), field));
+ ValueType valueType = ValueType.fromDexType(field.type);
+ instructions.add(new CfLoad(ValueType.OBJECT, 0));
+ instructions.add(new CfFieldInstruction(Opcodes.GETFIELD, field, field));
+ maxStack += valueType.requiredRegisters();
}
// Prepare arguments.
+ int maxLocals = 1; // Local 0 is the lambda/receiver.
for (int i = 0; i < erasedParams.length; i++) {
+ ValueType valueType = ValueType.fromDexType(mainMethod.getParameters().values[i]);
+ instructions.add(new CfLoad(valueType, maxLocals));
+ maxLocals += valueType.requiredRegisters();
DexType expectedParamType = implReceiverAndArgs.get(i + capturedValues);
- argValueTypes.add(ValueType.fromDexType(expectedParamType));
- argRegisters.add(prepareParameterValue(
- getParamRegister(i), erasedParams[i], enforcedParams[i], expectedParamType));
+ maxStack =
+ Math.max(
+ maxStack,
+ prepareParameterValue(
+ erasedParams[i], enforcedParams[i], expectedParamType, instructions, factory));
}
- // Method call to the method implementing lambda or method-ref.
- add(
- builder ->
- builder.addInvoke(
- target.invokeType,
- methodToCall,
- methodToCall.proto,
- argValueTypes,
- argRegisters,
- target.isInterface()));
+ instructions.add(
+ new CfInvoke(target.invokeType.getCfOpcode(), methodToCall, target.isInterface()));
+ DexType methodToCallReturnType = methodToCall.getReturnType();
+ if (!methodToCallReturnType.isVoidType()) {
+ maxStack =
+ Math.max(maxStack, ValueType.fromDexType(methodToCallReturnType).requiredRegisters());
+ }
- // Does the method have return value?
if (enforcedReturnType.isVoidType()) {
- add(IRBuilder::addReturn);
- } else if (constructorTarget) {
- // Return newly created instance
- int instanceRegister = argRegisters.get(0);
- int adjustedValue = prepareReturnValue(instanceRegister,
- erasedReturnType, enforcedReturnType, methodToCall.holder);
- add(builder -> builder.addReturn(adjustedValue));
+ if (!methodToCallReturnType.isVoidType()) {
+ instructions.add(
+ new CfStackInstruction(methodToCallReturnType.isWideType() ? Opcode.Pop2 : Opcode.Pop));
+ }
+ instructions.add(new CfReturnVoid());
} else {
- ValueType implValueType = ValueType.fromDexType(implReturnType);
- int tempValue = nextRegister(implValueType);
- add(builder -> builder.addMoveResult(tempValue));
- int adjustedValue = prepareReturnValue(tempValue,
- erasedReturnType, enforcedReturnType, methodToCall.proto.returnType);
- add(builder -> builder.addReturn(adjustedValue));
+ // Either the new instance or the called-method result is on top of stack.
+ assert constructorTarget || !methodToCallReturnType.isVoidType();
+ maxStack =
+ Math.max(
+ maxStack,
+ prepareReturnValue(
+ erasedReturnType,
+ enforcedReturnType,
+ constructorTarget ? methodToCall.holder : methodToCallReturnType,
+ instructions,
+ factory));
+ instructions.add(new CfReturn(ValueType.fromDexType(enforcedReturnType)));
}
+
+ ImmutableList<CfTryCatch> tryCatchRanges = ImmutableList.of();
+ ImmutableList<CfCode.LocalVariableInfo> localVariables = ImmutableList.of();
+ CfCode code =
+ new CfCode(
+ mainMethod.holder,
+ maxStack,
+ maxLocals,
+ instructions.build(),
+ tryCatchRanges,
+ localVariables);
+ return code;
+ }
+
+ private static CfCode buildThrowingCode(
+ DexMethod method, DexType exceptionType, DexItemFactory factory) {
+ DexProto initProto = factory.createProto(factory.voidType);
+ DexMethod initMethod =
+ factory.createMethod(exceptionType, initProto, factory.constructorMethodName);
+ int maxStack = 2;
+ int maxLocals = 1;
+ for (DexType param : method.proto.parameters.values) {
+ maxLocals += ValueType.fromDexType(param).requiredRegisters();
+ }
+ ImmutableList<CfTryCatch> tryCatchRanges = ImmutableList.of();
+ ImmutableList<CfCode.LocalVariableInfo> localVariables = ImmutableList.of();
+ return new CfCode(
+ method.holder,
+ maxStack,
+ maxLocals,
+ ImmutableList.of(
+ new CfNew(exceptionType),
+ new CfStackInstruction(Opcode.Dup),
+ new CfInvoke(Opcodes.INVOKESPECIAL, initMethod, false),
+ new CfThrow()),
+ tryCatchRanges,
+ localVariables);
}
// Adds necessary casts and transformations to adjust the value
// returned by impl-method to expected return type of the method.
- private int prepareReturnValue(int register,
- DexType erasedType, DexType enforcedType, DexType actualType) {
- // `actualType` must be adjusted to `enforcedType` first.
- register = adjustType(register, actualType, enforcedType, true);
-
+ private static int prepareReturnValue(
+ DexType erasedType,
+ DexType enforcedType,
+ DexType actualType,
+ Builder<CfInstruction> instructions,
+ DexItemFactory factory) {
// `erasedType` and `enforcedType` may only differ when they both
// are class types and `erasedType` is a base type of `enforcedType`,
// so no transformation is actually needed.
- assert LambdaDescriptor.isSameOrDerived(factory(), enforcedType, erasedType);
- return register;
+ assert LambdaDescriptor.isSameOrDerived(factory, enforcedType, erasedType);
+ return adjustType(actualType, enforcedType, true, instructions, factory);
}
// Adds necessary casts and transformations to adjust parameter
@@ -285,16 +332,50 @@
// be converted to enforced parameter type (`enforcedType`), which,
// in its turn, may need to be adjusted to the parameter type of
// the impl-method (`expectedType`).
- private int prepareParameterValue(int register,
- DexType erasedType, DexType enforcedType, DexType expectedType) {
- register = enforceParameterType(register, erasedType, enforcedType);
- register = adjustType(register, enforcedType, expectedType, false);
- return register;
+ private static int prepareParameterValue(
+ DexType erasedType,
+ DexType enforcedType,
+ DexType expectedType,
+ Builder<CfInstruction> instructions,
+ DexItemFactory factory) {
+ enforceParameterType(erasedType, enforcedType, instructions, factory);
+ return adjustType(enforcedType, expectedType, false, instructions, factory);
}
- private int adjustType(int register, DexType fromType, DexType toType, boolean returnType) {
+ private static void enforceParameterType(
+ DexType paramType,
+ DexType enforcedType,
+ Builder<CfInstruction> instructions,
+ DexItemFactory factory) {
+ // `paramType` must be either same as `enforcedType` or both must be class
+ // types and `enforcedType` must be a subclass of `paramType` in which case
+ // a cast need to be inserted.
+ if (paramType != enforcedType) {
+ assert LambdaDescriptor.isSameOrDerived(factory, enforcedType, paramType);
+ instructions.add(new CfCheckCast(enforcedType));
+ }
+ }
+
+ private static int adjustType(
+ DexType fromType,
+ DexType toType,
+ boolean returnType,
+ Builder<CfInstruction> instructions,
+ DexItemFactory factory) {
+ internalAdjustType(fromType, toType, returnType, instructions, factory);
+ return Math.max(
+ ValueType.fromDexType(fromType).requiredRegisters(),
+ ValueType.fromDexType(toType).requiredRegisters());
+ }
+
+ private static void internalAdjustType(
+ DexType fromType,
+ DexType toType,
+ boolean returnType,
+ Builder<CfInstruction> instructions,
+ DexItemFactory factory) {
if (fromType == toType) {
- return register;
+ return;
}
boolean fromTypePrimitive = fromType.isPrimitiveType();
@@ -302,7 +383,8 @@
// If both are primitive they must be convertible via primitive widening conversion.
if (fromTypePrimitive && toTypePrimitive) {
- return addPrimitiveWideningConversion(register, fromType, toType);
+ addPrimitiveWideningConversion(fromType, toType, instructions);
+ return;
}
// If the first one is a boxed primitive type and the second one is a primitive
@@ -310,62 +392,64 @@
// widening conversion.
if (toTypePrimitive) {
DexType boxedType = fromType;
- if (boxedType == factory().objectType) {
+ if (boxedType == factory.objectType) {
// We are in situation when from(=java.lang.Object) is being adjusted to a
// primitive type, in which case we assume it is of proper box type.
- boxedType = getBoxedForPrimitiveType(toType);
- register = castToBoxedType(register, boxedType);
+ boxedType = getBoxedForPrimitiveType(toType, factory);
+ instructions.add(new CfCheckCast(boxedType));
}
- DexType fromTypeAsPrimitive = getPrimitiveFromBoxed(boxedType);
+ DexType fromTypeAsPrimitive = factory.getPrimitiveFromBoxed(boxedType);
if (fromTypeAsPrimitive != null) {
- int unboxedRegister = addPrimitiveUnboxing(register, fromTypeAsPrimitive, boxedType);
- return addPrimitiveWideningConversion(unboxedRegister, fromTypeAsPrimitive, toType);
+ addPrimitiveUnboxing(fromTypeAsPrimitive, boxedType, instructions, factory);
+ addPrimitiveWideningConversion(fromTypeAsPrimitive, toType, instructions);
+ return;
}
}
// If the first one is a primitive type and the second one is a boxed
// type for this primitive type, just box the value.
if (fromTypePrimitive) {
- DexType boxedFromType = getBoxedForPrimitiveType(fromType);
- if (toType == boxedFromType ||
- toType == factory().objectType ||
- toType == factory().serializableType ||
- toType == factory().comparableType ||
- (boxedFromType != factory().booleanType &&
- boxedFromType != factory().charType &&
- toType == factory().boxedNumberType)) {
- return addPrimitiveBoxing(register, fromType, boxedFromType);
+ DexType boxedFromType = getBoxedForPrimitiveType(fromType, factory);
+ if (toType == boxedFromType
+ || toType == factory.objectType
+ || toType == factory.serializableType
+ || toType == factory.comparableType
+ || (boxedFromType != factory.booleanType
+ && boxedFromType != factory.charType
+ && toType == factory.boxedNumberType)) {
+ addPrimitiveBoxing(fromType, boxedFromType, instructions, factory);
+ return;
}
}
- if (fromType.isArrayType() && (toType == factory().objectType || toType.isArrayType())) {
+ if (fromType.isArrayType() && (toType == factory.objectType || toType.isArrayType())) {
// If `fromType` is an array and `toType` is java.lang.Object, no cast is needed.
// If both `fromType` and `toType` are arrays, no cast is needed since we assume
// the input code is verifiable.
- return register;
+ return;
}
if ((fromType.isClassType() && toType.isClassType())
- || (fromType == factory().objectType && toType.isArrayType())) {
- if (returnType) {
+ || (fromType == factory.objectType && toType.isArrayType())) {
+ if (returnType && toType != factory.objectType) {
// For return type adjustment in case `fromType` and `toType` are both reference types,
// `fromType` does NOT have to be deriving from `toType` and we need to add a cast.
// NOTE: we don't check `toType` for being actually a supertype, since we
// might not have full classpath with inheritance information to do that.
- int finalRegister = register;
- add(builder -> builder.addCheckCast(finalRegister, toType));
+ instructions.add(new CfCheckCast(toType));
}
- return register;
+ return;
}
throw new Unreachable("Unexpected type adjustment from "
+ fromType.toSourceString() + " to " + toType);
}
- private int addPrimitiveWideningConversion(int register, DexType fromType, DexType toType) {
+ private static void addPrimitiveWideningConversion(
+ DexType fromType, DexType toType, Builder<CfInstruction> instructions) {
assert fromType.isPrimitiveType() && toType.isPrimitiveType();
if (fromType == toType) {
- return register;
+ return;
}
NumericType from = NumericType.fromDexType(fromType);
@@ -379,14 +463,13 @@
if (from != NumericType.BYTE) {
break; // Only BYTE can be converted to SHORT via widening conversion.
}
- int result = nextRegister(ValueType.INT);
- add(builder -> builder.addConversion(to, NumericType.INT, result, register));
- return result;
+ instructions.add(new CfNumberConversion(NumericType.INT, to));
+ return;
}
case INT:
if (from == NumericType.BYTE || from == NumericType.CHAR || from == NumericType.SHORT) {
- return register; // No actual conversion is needed.
+ return;
}
break;
@@ -394,27 +477,24 @@
if (from == NumericType.FLOAT || from == NumericType.DOUBLE) {
break; // Not a widening conversion.
}
- int result = nextRegister(ValueType.LONG);
- add(builder -> builder.addConversion(to, NumericType.INT, result, register));
- return result;
+ instructions.add(new CfNumberConversion(NumericType.INT, to));
+ return;
}
case FLOAT: {
if (from == NumericType.DOUBLE) {
break; // Not a widening conversion.
}
- int result = nextRegister(ValueType.FLOAT);
NumericType type = (from == NumericType.LONG) ? NumericType.LONG : NumericType.INT;
- add(builder -> builder.addConversion(to, type, result, register));
- return result;
+ instructions.add(new CfNumberConversion(type, to));
+ return;
}
case DOUBLE: {
- int result = nextRegister(ValueType.DOUBLE);
NumericType type = (from == NumericType.FLOAT || from == NumericType.LONG)
? from : NumericType.INT;
- add(builder -> builder.addConversion(to, type, result, register));
- return result;
+ instructions.add(new CfNumberConversion(type, to));
+ return;
}
default:
// exception is thrown below
@@ -426,8 +506,7 @@
"converted to " + toType.toSourceString() + " via primitive widening conversion.");
}
- private DexMethod getUnboxMethod(byte primitive, DexType boxType) {
- DexItemFactory factory = factory();
+ private static DexMethod getUnboxMethod(byte primitive, DexType boxType, DexItemFactory factory) {
DexProto proto;
switch (primitive) {
case 'Z': // byte
@@ -459,53 +538,23 @@
}
}
- private int addPrimitiveUnboxing(int register, DexType primitiveType, DexType boxType) {
- DexMethod method = getUnboxMethod(primitiveType.descriptor.content[0], boxType);
-
- List<ValueType> argValueTypes = ImmutableList.of(ValueType.OBJECT);
- List<Integer> argRegisters = Collections.singletonList(register);
- add(
- builder ->
- builder.addInvoke(
- Invoke.Type.VIRTUAL,
- method,
- method.proto,
- argValueTypes,
- argRegisters,
- false /* isInterface */));
-
- ValueType valueType = ValueType.fromDexType(primitiveType);
- int result = nextRegister(valueType);
- add(builder -> builder.addMoveResult(result));
- return result;
+ private static void addPrimitiveUnboxing(
+ DexType primitiveType,
+ DexType boxType,
+ Builder<CfInstruction> instructions,
+ DexItemFactory factory) {
+ DexMethod method = getUnboxMethod(primitiveType.descriptor.content[0], boxType, factory);
+ instructions.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, method, false));
}
- private int castToBoxedType(int register, DexType boxType) {
- add(builder -> builder.addCheckCast(register, boxType));
- return register;
- }
-
- private int addPrimitiveBoxing(int register, DexType primitiveType, DexType boxType) {
+ private static void addPrimitiveBoxing(
+ DexType primitiveType,
+ DexType boxType,
+ Builder<CfInstruction> instructions,
+ DexItemFactory factory) {
// Generate factory method fo boxing.
- DexItemFactory factory = factory();
DexProto proto = factory.createProto(boxType, primitiveType);
DexMethod method = factory.createMethod(boxType, proto, factory.valueOfMethodName);
-
- ValueType valueType = ValueType.fromDexType(primitiveType);
- List<ValueType> argValueTypes = ImmutableList.of(valueType);
- List<Integer> argRegisters = Collections.singletonList(register);
- add(
- builder ->
- builder.addInvoke(
- Invoke.Type.STATIC,
- method,
- method.proto,
- argValueTypes,
- argRegisters,
- false /* isInterface */));
-
- int result = nextRegister(ValueType.OBJECT);
- add(builder -> builder.addMoveResult(result));
- return result;
+ instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, method, false));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSynthesizedCode.java
deleted file mode 100644
index 7c24b63..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSynthesizedCode.java
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (c) 2019, 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;
-
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.UseRegistry;
-import com.android.tools.r8.ir.code.Invoke;
-import com.android.tools.r8.ir.code.Invoke.Type;
-import java.util.function.Consumer;
-
-public class LambdaMainMethodSynthesizedCode extends LambdaSynthesizedCode {
-
- private final DexMethod mainMethod;
-
- LambdaMainMethodSynthesizedCode(LambdaClass lambda, DexMethod mainMethod) {
- super(lambda);
- this.mainMethod = mainMethod;
- }
-
- @Override
- public SourceCodeProvider getSourceCodeProvider() {
- return callerPosition -> new LambdaMainMethodSourceCode(lambda, mainMethod, callerPosition);
- }
-
- @Override
- public Consumer<UseRegistry> getRegistryCallback() {
- return registry -> {
- LambdaClass.Target target = lambda.target;
- assert target.invokeType == Invoke.Type.STATIC
- || target.invokeType == Invoke.Type.VIRTUAL
- || target.invokeType == Invoke.Type.DIRECT
- || target.invokeType == Invoke.Type.INTERFACE;
-
- boolean constructorTarget = target.invokeType == Type.DIRECT;
- if (constructorTarget) {
- registry.registerNewInstance(target.callTarget.holder);
- }
-
- DexType[] capturedTypes = captures();
- for (int i = 0; i < capturedTypes.length; i++) {
- registry.registerInstanceFieldRead(lambda.getCaptureField(i));
- }
-
- switch (target.invokeType) {
- case DIRECT:
- registry.registerInvokeDirect(target.callTarget);
- break;
- case INTERFACE:
- registry.registerInvokeInterface(target.callTarget);
- break;
- case STATIC:
- registry.registerInvokeStatic(target.callTarget);
- break;
- case VIRTUAL:
- registry.registerInvokeVirtual(target.callTarget);
- break;
- default:
- throw new Unreachable();
- }
- };
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaSynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaSynthesizedCode.java
deleted file mode 100644
index 5643288..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaSynthesizedCode.java
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) 2019, 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;
-
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.graph.UseRegistry;
-import com.android.tools.r8.ir.synthetic.SynthesizedCode;
-import java.util.function.Consumer;
-
-abstract class LambdaSynthesizedCode extends SynthesizedCode {
-
- final LambdaClass lambda;
-
- LambdaSynthesizedCode(LambdaClass lambda) {
- super(null);
- this.lambda = lambda;
- }
-
- final DexItemFactory dexItemFactory() {
- return lambda.appView.dexItemFactory();
- }
-
- final LambdaDescriptor descriptor() {
- return lambda.descriptor;
- }
-
- final DexType[] captures() {
- DexTypeList captures = descriptor().captures;
- assert captures != null;
- return captures.values;
- }
-
- @Override
- public abstract SourceCodeProvider getSourceCodeProvider();
-
- @Override
- public abstract Consumer<UseRegistry> getRegistryCallback();
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java
deleted file mode 100644
index fc12713..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/SynthesizedLambdaSourceCode.java
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (c) 2017, 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;
-
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
-
-// Represents source code of synthesized lambda class methods.
-abstract class SynthesizedLambdaSourceCode extends SyntheticSourceCode {
-
- final DexMethod currentMethod;
- final LambdaClass lambda;
-
- SynthesizedLambdaSourceCode(
- LambdaClass lambda, DexMethod currentMethod, Position callerPosition, DexType receiver) {
- super(receiver, currentMethod, callerPosition);
- this.lambda = lambda;
- this.currentMethod = currentMethod;
- }
-
- SynthesizedLambdaSourceCode(
- LambdaClass lambda, DexMethod currentMethod, Position callerPosition) {
- this(lambda, currentMethod, callerPosition, lambda.type);
- }
-
- final LambdaDescriptor descriptor() {
- return lambda.descriptor;
- }
-
- final DexType[] captures() {
- DexTypeList captures = descriptor().captures;
- assert captures != null;
- return captures.values;
- }
-
- final DexItemFactory factory() {
- return lambda.appView.dexItemFactory();
- }
-
- final int enforceParameterType(int register, DexType paramType, DexType enforcedType) {
- // `paramType` must be either same as `enforcedType` or both must be class
- // types and `enforcedType` must be a subclass of `paramType` in which case
- // a cast need to be inserted.
- if (paramType != enforcedType) {
- assert LambdaDescriptor.isSameOrDerived(factory(), enforcedType, paramType);
- add(builder -> builder.addCheckCast(register, enforcedType));
- }
- return register;
- }
-
- @Override
- public String toString() {
- return currentMethod.toSourceString();
- }
-}
-
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BooleanMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BooleanMethodRewrites.java
index 90654b2..ec0a554 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BooleanMethodRewrites.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BooleanMethodRewrites.java
@@ -4,52 +4,27 @@
package com.android.tools.r8.ir.desugar.backports;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.ir.code.And;
-import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.cf.code.CfLogicalBinop;
+import com.android.tools.r8.cf.code.CfLogicalBinop.Opcode;
import com.android.tools.r8.ir.code.NumericType;
-import com.android.tools.r8.ir.code.Or;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.code.Xor;
-import java.util.List;
-import java.util.Set;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
public final class BooleanMethodRewrites {
- public static void rewriteLogicalAnd(
- InvokeMethod invoke,
- InstructionListIterator iterator,
- DexItemFactory factory,
- Set<Value> affectedValues) {
- List<Value> inValues = invoke.inValues();
- assert inValues.size() == 2;
- iterator.replaceCurrentInstruction(
- new And(NumericType.INT, invoke.outValue(), inValues.get(0), inValues.get(1)));
+ private static MethodInvokeRewriter createRewriter(CfLogicalBinop.Opcode op) {
+ return (invoke, factory) -> new CfLogicalBinop(op, NumericType.INT);
}
- public static void rewriteLogicalOr(
- InvokeMethod invoke,
- InstructionListIterator iterator,
- DexItemFactory factory,
- Set<Value> affectedValues) {
- List<Value> inValues = invoke.inValues();
- assert inValues.size() == 2;
-
- iterator.replaceCurrentInstruction(
- new Or(NumericType.INT, invoke.outValue(), inValues.get(0), inValues.get(1)));
+ public static MethodInvokeRewriter rewriteLogicalAnd() {
+ return createRewriter(Opcode.And);
}
- public static void rewriteLogicalXor(
- InvokeMethod invoke,
- InstructionListIterator iterator,
- DexItemFactory factory,
- Set<Value> affectedValues) {
- List<Value> inValues = invoke.inValues();
- assert inValues.size() == 2;
+ public static MethodInvokeRewriter rewriteLogicalOr() {
+ return createRewriter(Opcode.Or);
+ }
- iterator.replaceCurrentInstruction(
- new Xor(NumericType.INT, invoke.outValue(), inValues.get(0), inValues.get(1)));
+ public static MethodInvokeRewriter rewriteLogicalXor() {
+ return createRewriter(Opcode.Xor);
}
private BooleanMethodRewrites() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/CollectionMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/CollectionMethodRewrites.java
index 354040f..64c94c5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/CollectionMethodRewrites.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/CollectionMethodRewrites.java
@@ -4,54 +4,31 @@
package com.android.tools.r8.ir.desugar.backports;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeStatic;
-import com.android.tools.r8.ir.code.Value;
-import java.util.Collections;
-import java.util.Set;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
+import org.objectweb.asm.Opcodes;
public final class CollectionMethodRewrites {
private CollectionMethodRewrites() {}
- public static void rewriteListOfEmpty(
- InvokeMethod invoke,
- InstructionListIterator iterator,
- DexItemFactory factory,
- Set<Value> affectedValues) {
- rewriteToCollectionMethod(invoke, iterator, factory, "emptyList");
+ public static MethodInvokeRewriter rewriteListOfEmpty() {
+ return rewriteToCollectionMethod("emptyList");
}
- public static void rewriteSetOfEmpty(
- InvokeMethod invoke,
- InstructionListIterator iterator,
- DexItemFactory factory,
- Set<Value> affectedValues) {
- rewriteToCollectionMethod(invoke, iterator, factory, "emptySet");
+ public static MethodInvokeRewriter rewriteSetOfEmpty() {
+ return rewriteToCollectionMethod("emptySet");
}
- public static void rewriteMapOfEmpty(
- InvokeMethod invoke,
- InstructionListIterator iterator,
- DexItemFactory factory,
- Set<Value> affectedValues) {
- rewriteToCollectionMethod(invoke, iterator, factory, "emptyMap");
+ public static MethodInvokeRewriter rewriteMapOfEmpty() {
+ return rewriteToCollectionMethod("emptyMap");
}
- private static void rewriteToCollectionMethod(
- InvokeMethod invoke,
- InstructionListIterator iterator,
- DexItemFactory factory,
- String methodName) {
- assert invoke.inValues().isEmpty();
-
- DexMethod collectionsEmptyList =
- factory.createMethod(factory.collectionsType, invoke.getInvokedMethod().proto, methodName);
- InvokeStatic newInvoke =
- new InvokeStatic(collectionsEmptyList, invoke.outValue(), Collections.emptyList());
- iterator.replaceCurrentInstruction(newInvoke);
+ private static MethodInvokeRewriter rewriteToCollectionMethod(String methodName) {
+ return (invoke, factory) ->
+ new CfInvoke(
+ Opcodes.INVOKESTATIC,
+ factory.createMethod(factory.collectionsType, invoke.getMethod().proto, methodName),
+ false);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/FloatMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/FloatMethodRewrites.java
index 3601c51..8670d3b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/FloatMethodRewrites.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/FloatMethodRewrites.java
@@ -4,29 +4,20 @@
package com.android.tools.r8.ir.desugar.backports;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeStatic;
-import com.android.tools.r8.ir.code.Value;
-import java.util.Set;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
+import org.objectweb.asm.Opcodes;
public final class FloatMethodRewrites {
private FloatMethodRewrites() {}
- public static void rewriteHashCode(
- InvokeMethod invoke,
- InstructionListIterator iterator,
- DexItemFactory factory,
- Set<Value> affectedValues) {
- InvokeStatic mathInvoke =
- new InvokeStatic(
+ public static MethodInvokeRewriter rewriteHashCode() {
+ return (invoke, factory) ->
+ new CfInvoke(
+ Opcodes.INVOKESTATIC,
factory.createMethod(
- factory.boxedFloatType, invoke.getInvokedMethod().proto, "floatToIntBits"),
- invoke.outValue(),
- invoke.inValues(),
+ factory.boxedFloatType, invoke.getMethod().proto, "floatToIntBits"),
false);
- iterator.replaceCurrentInstruction(mathInvoke);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/LongMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/LongMethodRewrites.java
index 348194b..5c29f81 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/LongMethodRewrites.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/LongMethodRewrites.java
@@ -4,28 +4,16 @@
package com.android.tools.r8.ir.desugar.backports;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.ir.code.Cmp;
+import com.android.tools.r8.cf.code.CfCmp;
import com.android.tools.r8.ir.code.Cmp.Bias;
-import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.NumericType;
-import com.android.tools.r8.ir.code.Value;
-import java.util.List;
-import java.util.Set;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
public final class LongMethodRewrites {
private LongMethodRewrites() {}
- public static void rewriteCompare(
- InvokeMethod invoke,
- InstructionListIterator iterator,
- DexItemFactory factory,
- Set<Value> affectedValues) {
- List<Value> inValues = invoke.inValues();
- assert inValues.size() == 2;
- iterator.replaceCurrentInstruction(
- new Cmp(NumericType.LONG, Bias.NONE, invoke.outValue(), inValues.get(0), inValues.get(1)));
+ public static MethodInvokeRewriter rewriteCompare() {
+ return (invoke, factory) -> new CfCmp(Bias.NONE, NumericType.LONG);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/NumericMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/NumericMethodRewrites.java
index 4a413bc..050aefa 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/NumericMethodRewrites.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/NumericMethodRewrites.java
@@ -1,56 +1,44 @@
package com.android.tools.r8.ir.desugar.backports;
+import com.android.tools.r8.cf.code.CfArithmeticBinop;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.ir.code.Add;
-import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.ir.code.NumericType;
-import com.android.tools.r8.ir.code.Value;
-import java.util.List;
-import java.util.Set;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.FullMethodInvokeRewriter;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
+import java.util.ListIterator;
+import org.objectweb.asm.Opcodes;
public final class NumericMethodRewrites {
- public static void rewriteToInvokeMath(
- InvokeMethod invoke,
- InstructionListIterator iterator,
- DexItemFactory factory,
- Set<Value> affectedValues) {
- InvokeStatic mathInvoke =
- new InvokeStatic(
- factory.createMethod(
- factory.mathType, invoke.getInvokedMethod().proto, invoke.getInvokedMethod().name),
- invoke.outValue(),
- invoke.inValues(),
- false);
- iterator.replaceCurrentInstruction(mathInvoke);
+
+ public static MethodInvokeRewriter rewriteToInvokeMath() {
+ return (invoke, factory) -> {
+ DexMethod method = invoke.getMethod();
+ return new CfInvoke(
+ Opcodes.INVOKESTATIC,
+ factory.createMethod(factory.mathType, method.proto, method.name),
+ false);
+ };
}
- public static void rewriteToAddInstruction(
- InvokeMethod invoke,
- InstructionListIterator iterator,
- DexItemFactory factory,
- Set<Value> affectedValues) {
- List<Value> values = invoke.inValues();
- assert values.size() == 2;
-
- NumericType numericType = NumericType.fromDexType(invoke.getReturnType());
- Add add = new Add(numericType, invoke.outValue(), values.get(0), values.get(1));
- iterator.replaceCurrentInstruction(add);
+ public static MethodInvokeRewriter rewriteToAddInstruction() {
+ return (invoke, factory) -> {
+ NumericType numericType = NumericType.fromDexType(invoke.getMethod().getReturnType());
+ return new CfArithmeticBinop(CfArithmeticBinop.Opcode.Add, numericType);
+ };
}
- public static void rewriteAsIdentity(
- InvokeMethod invoke,
- InstructionListIterator iterator,
- DexItemFactory factory,
- Set<Value> affectedValues) {
- List<Value> values = invoke.inValues();
- assert values.size() == 1;
- if (invoke.hasOutValue()) {
- invoke.outValue().replaceUsers(values.get(0));
- }
- // TODO(b/152853271): Debugging information is lost here (DebugLocalWrite may be required).
- iterator.removeOrReplaceByDebugLocalRead();
+ public static MethodInvokeRewriter rewriteAsIdentity() {
+ return new FullMethodInvokeRewriter() {
+ @Override
+ public void rewrite(
+ CfInvoke invoke, ListIterator<CfInstruction> iterator, DexItemFactory factory) {
+ // The invoke consumes the stack value and pushes another assumed to be the same.
+ iterator.remove();
+ }
+ };
}
private NumericMethodRewrites() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethodRewrites.java
index de16e98..69253be 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethodRewrites.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/ObjectsMethodRewrites.java
@@ -4,43 +4,41 @@
package com.android.tools.r8.ir.desugar.backports;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeStatic;
-import com.android.tools.r8.ir.code.InvokeVirtual;
-import com.android.tools.r8.ir.code.Value;
-import java.util.Set;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.FullMethodInvokeRewriter;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
+import java.util.ListIterator;
+import org.objectweb.asm.Opcodes;
public final class ObjectsMethodRewrites {
- public static void rewriteToArraysHashCode(
- InvokeMethod invoke,
- InstructionListIterator iterator,
- DexItemFactory factory,
- Set<Value> affectedValues) {
- DexType arraysType = factory.createType(factory.arraysDescriptor);
- DexMethod hashCodeMethod =
- factory.createMethod(arraysType, invoke.getInvokedMethod().proto, "hashCode");
- InvokeStatic arraysHashCode =
- new InvokeStatic(hashCodeMethod, invoke.outValue(), invoke.inValues(), false);
- iterator.replaceCurrentInstruction(arraysHashCode);
+ public static MethodInvokeRewriter rewriteToArraysHashCode() {
+ return (invoke, factory) -> {
+ DexType arraysType = factory.createType(factory.arraysDescriptor);
+ return new CfInvoke(
+ Opcodes.INVOKESTATIC,
+ factory.createMethod(arraysType, invoke.getMethod().proto, "hashCode"),
+ false);
+ };
}
- public static void rewriteRequireNonNull(
- InvokeMethod invoke,
- InstructionListIterator iterator,
- DexItemFactory factory,
- Set<Value> affectedValues) {
- InvokeVirtual getClass =
- new InvokeVirtual(factory.objectMembers.getClass, null, invoke.inValues());
- if (invoke.hasOutValue()) {
- affectedValues.addAll(invoke.outValue().affectedValues());
- invoke.outValue().replaceUsers(invoke.inValues().get(0));
- invoke.setOutValue(null);
- }
- iterator.replaceCurrentInstruction(getClass);
+ public static MethodInvokeRewriter rewriteRequireNonNull() {
+ return new FullMethodInvokeRewriter() {
+
+ @Override
+ public void rewrite(
+ CfInvoke invoke, ListIterator<CfInstruction> iterator, DexItemFactory factory) {
+ iterator.remove();
+ // requireNonNull returns the operand, so dup top-of-stack, do getClass and pop the class.
+ iterator.add(new CfStackInstruction(Opcode.Dup));
+ iterator.add(new CfInvoke(Opcodes.INVOKEVIRTUAL, factory.objectMembers.getClass, false));
+ iterator.add(new CfStackInstruction(Opcode.Pop));
+ }
+ };
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/OptionalMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/OptionalMethodRewrites.java
index 1334c62..d66d7f5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/OptionalMethodRewrites.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/OptionalMethodRewrites.java
@@ -4,69 +4,40 @@
package com.android.tools.r8.ir.desugar.backports;
+import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeVirtual;
-import com.android.tools.r8.ir.code.Value;
-import java.util.Set;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
+import java.util.function.Function;
+import org.objectweb.asm.Opcodes;
public final class OptionalMethodRewrites {
private OptionalMethodRewrites() {}
- public static void rewriteOrElseGet(
- InvokeMethod invoke,
- InstructionListIterator iterator,
- DexItemFactory factory,
- Set<Value> affectedValues) {
- InvokeVirtual getInvoke =
- new InvokeVirtual(
- factory.createMethod(factory.optionalType, invoke.getInvokedMethod().proto, "get"),
- invoke.outValue(),
- invoke.inValues());
- iterator.replaceCurrentInstruction(getInvoke);
+ private static MethodInvokeRewriter createRewriter(
+ Function<DexItemFactory, DexType> holderTypeSupplier, String methodName) {
+ return (invoke, factory) ->
+ new CfInvoke(
+ Opcodes.INVOKEVIRTUAL,
+ factory.createMethod(
+ holderTypeSupplier.apply(factory), invoke.getMethod().proto, methodName),
+ false);
}
- public static void rewriteDoubleOrElseGet(
- InvokeMethod invoke,
- InstructionListIterator iterator,
- DexItemFactory factory,
- Set<Value> affectedValues) {
- InvokeVirtual getInvoke =
- new InvokeVirtual(
- factory.createMethod(
- factory.optionalDoubleType, invoke.getInvokedMethod().proto, "getAsDouble"),
- invoke.outValue(),
- invoke.inValues());
- iterator.replaceCurrentInstruction(getInvoke);
+ public static MethodInvokeRewriter rewriteOrElseGet() {
+ return createRewriter(factory -> factory.optionalType, "get");
}
- public static void rewriteIntOrElseGet(
- InvokeMethod invoke,
- InstructionListIterator iterator,
- DexItemFactory factory,
- Set<Value> affectedValues) {
- InvokeVirtual getInvoke =
- new InvokeVirtual(
- factory.createMethod(
- factory.optionalIntType, invoke.getInvokedMethod().proto, "getAsInt"),
- invoke.outValue(),
- invoke.inValues());
- iterator.replaceCurrentInstruction(getInvoke);
+ public static MethodInvokeRewriter rewriteDoubleOrElseGet() {
+ return createRewriter(factory -> factory.optionalDoubleType, "getAsDouble");
}
- public static void rewriteLongOrElseGet(
- InvokeMethod invoke,
- InstructionListIterator iterator,
- DexItemFactory factory,
- Set<Value> affectedValues) {
- InvokeVirtual getInvoke =
- new InvokeVirtual(
- factory.createMethod(
- factory.optionalLongType, invoke.getInvokedMethod().proto, "getAsLong"),
- invoke.outValue(),
- invoke.inValues());
- iterator.replaceCurrentInstruction(getInvoke);
+ public static MethodInvokeRewriter rewriteIntOrElseGet() {
+ return createRewriter(factory -> factory.optionalIntType, "getAsInt");
+ }
+
+ public static MethodInvokeRewriter rewriteLongOrElseGet() {
+ return createRewriter(factory -> factory.optionalLongType, "getAsLong");
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index c937356..4a2c485 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.optimize.enums;
+import static com.android.tools.r8.graph.GenericSignature.NO_FIELD_TYPE_SIGNATURE;
import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
import com.android.tools.r8.dex.Constants;
@@ -421,6 +422,7 @@
field,
FieldAccessFlags.fromSharedAccessFlags(
Constants.ACC_SYNTHETIC | Constants.ACC_STATIC | Constants.ACC_PUBLIC),
+ NO_FIELD_TYPE_SIGNATURE,
DexAnnotationSet.empty(),
null);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
index db7f56c..a29db28 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.optimize.lambda.kotlin;
+import static com.android.tools.r8.graph.GenericSignature.NO_FIELD_TYPE_SIGNATURE;
import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -288,12 +289,22 @@
int size = capture.length();
DexEncodedField[] result = new DexEncodedField[1 + size];
- result[0] = new DexEncodedField(group.getLambdaIdField(factory),
- CAPTURE_FIELD_FLAGS_RELAXED, DexAnnotationSet.empty(), null);
+ result[0] =
+ new DexEncodedField(
+ group.getLambdaIdField(factory),
+ CAPTURE_FIELD_FLAGS_RELAXED,
+ NO_FIELD_TYPE_SIGNATURE,
+ DexAnnotationSet.empty(),
+ null);
for (int id = 0; id < size; id++) {
- result[id + 1] = new DexEncodedField(group.getCaptureField(factory, id),
- CAPTURE_FIELD_FLAGS_RELAXED, DexAnnotationSet.empty(), null);
+ result[id + 1] =
+ new DexEncodedField(
+ group.getCaptureField(factory, id),
+ CAPTURE_FIELD_FLAGS_RELAXED,
+ NO_FIELD_TYPE_SIGNATURE,
+ DexAnnotationSet.empty(),
+ null);
}
return result;
@@ -313,7 +324,11 @@
DexField field = group.getSingletonInstanceField(factory, info.id);
DexEncodedField encodedField =
new DexEncodedField(
- field, SINGLETON_FIELD_FLAGS, DexAnnotationSet.empty(), DexValueNull.NULL);
+ field,
+ SINGLETON_FIELD_FLAGS,
+ NO_FIELD_TYPE_SIGNATURE,
+ DexAnnotationSet.empty(),
+ DexValueNull.NULL);
result.add(encodedField);
// Record that the field is definitely not null. It is guaranteed to be assigned in the
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryFieldSynthesis.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryFieldSynthesis.java
index 3f3277f..a20b8bf 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryFieldSynthesis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryFieldSynthesis.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize.library;
import static com.android.tools.r8.graph.DexLibraryClass.asLibraryClassOrNull;
+import static com.android.tools.r8.graph.GenericSignature.NO_FIELD_TYPE_SIGNATURE;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
@@ -38,6 +39,7 @@
field,
FieldAccessFlags.fromCfAccessFlags(
Constants.ACC_PRIVATE | Constants.ACC_FINAL),
+ NO_FIELD_TYPE_SIGNATURE,
DexAnnotationSet.empty(),
null));
}
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
new file mode 100644
index 0000000..8d8ca9f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
@@ -0,0 +1,243 @@
+// Copyright (c) 2020, 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.synthetic;
+
+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.CfReturn;
+import com.android.tools.r8.cf.code.CfReturnVoid;
+import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
+import com.android.tools.r8.cf.code.CfTryCatch;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.ValueType;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import org.objectweb.asm.Opcodes;
+
+public class ForwardMethodBuilder {
+
+ public static ForwardMethodBuilder builder(DexItemFactory factory) {
+ return new ForwardMethodBuilder(factory);
+ }
+
+ private enum InvokeType {
+ STATIC,
+ VIRTUAL,
+ SPECIAL
+ }
+
+ private final DexItemFactory factory;
+
+ private DexMethod sourceMethod = null;
+ private DexMethod targetMethod = null;
+
+ private boolean staticSource = false;
+
+ private InvokeType invokeType = null;
+ private Boolean isInterface = null;
+ private boolean castResult = false;
+ private boolean isConstructorDelegate = false;
+ private AppInfoWithClassHierarchy appInfoForCastArguments = null;
+
+ private ForwardMethodBuilder(DexItemFactory factory) {
+ this.factory = factory;
+ }
+
+ public ForwardMethodBuilder setNonStaticSource(DexMethod method) {
+ sourceMethod = method;
+ staticSource = false;
+ return this;
+ }
+
+ public ForwardMethodBuilder setStaticSource(DexMethod method) {
+ sourceMethod = method;
+ staticSource = true;
+ return this;
+ }
+
+ public ForwardMethodBuilder setStaticTarget(DexMethod method, boolean isInterface) {
+ targetMethod = method;
+ invokeType = InvokeType.STATIC;
+ this.isInterface = isInterface;
+ return this;
+ }
+
+ public ForwardMethodBuilder setVirtualTarget(DexMethod method, boolean isInterface) {
+ targetMethod = method;
+ invokeType = InvokeType.VIRTUAL;
+ this.isInterface = isInterface;
+ return this;
+ }
+
+ public ForwardMethodBuilder setDirectTarget(DexMethod method, boolean isInterface) {
+ targetMethod = method;
+ invokeType = InvokeType.SPECIAL;
+ this.isInterface = isInterface;
+ return this;
+ }
+
+ public ForwardMethodBuilder setCastResult() {
+ castResult = true;
+ return this;
+ }
+
+ public ForwardMethodBuilder setCastArguments(AppInfoWithClassHierarchy appInfo) {
+ appInfoForCastArguments = appInfo;
+ return this;
+ }
+
+ public ForwardMethodBuilder setConstructorTarget(DexMethod method, DexItemFactory factory) {
+ assert method.isInstanceInitializer(factory);
+ targetMethod = method;
+ isConstructorDelegate = true;
+ invokeType = InvokeType.SPECIAL;
+ isInterface = false;
+ return this;
+ }
+
+ public CfCode build() {
+ assert validate();
+ int maxStack = 0;
+ int maxLocals = 0;
+ Builder<CfInstruction> instructions = ImmutableList.builder();
+ if (isConstructorDelegate) {
+ // A constructor delegate allocates a new instance of the type.
+ // It is dup'ed on the stack so it is ready to return after the invoke call.
+ assert isStaticSource();
+ assert invokeType == InvokeType.SPECIAL;
+ instructions.add(new CfNew(targetMethod.getHolderType()));
+ instructions.add(new CfStackInstruction(Opcode.Dup));
+ maxStack += 2;
+ } else if (!isStaticSource()) {
+ // If source is not static, load the receiver.
+ instructions.add(new CfLoad(ValueType.OBJECT, maxLocals));
+ maybeInsertArgumentCast(-1, sourceMethod.holder, instructions);
+ maxStack += 1;
+ maxLocals += 1;
+ }
+ DexType[] sourceParameters = getSourceParameters();
+ for (int i = 0; i < sourceParameters.length; i++) {
+ DexType parameter = sourceParameters[i];
+ ValueType parameterType = ValueType.fromDexType(parameter);
+ instructions.add(new CfLoad(parameterType, maxLocals));
+ maxLocals += parameterType.requiredRegisters();
+ maybeInsertArgumentCast(i, parameter, instructions);
+ }
+ instructions.add(new CfInvoke(getInvokeOpcode(), targetMethod, isInterface));
+ if (isSourceReturnVoid()) {
+ assert !isConstructorDelegate;
+ instructions.add(new CfReturnVoid());
+ } else {
+ if (!isConstructorDelegate && sourceMethod.getReturnType() != targetMethod.getReturnType()) {
+ assert castResult;
+ if (sourceMethod.getReturnType() != factory.objectType) {
+ instructions.add(new CfCheckCast(sourceMethod.getReturnType()));
+ }
+ }
+ instructions.add(new CfReturn(getSourceReturnType()));
+ }
+ ImmutableList<CfTryCatch> tryCatchRanges = ImmutableList.of();
+ ImmutableList<CfCode.LocalVariableInfo> localVariables = ImmutableList.of();
+ return new CfCode(
+ sourceMethod.holder,
+ maxStack,
+ maxLocals,
+ instructions.build(),
+ tryCatchRanges,
+ localVariables);
+ }
+
+ private void maybeInsertArgumentCast(
+ int argumentIndex, DexType sourceArgumentType, Builder<CfInstruction> instructions) {
+ if (appInfoForCastArguments == null) {
+ return;
+ }
+ // Shift argument index if mapping between static and non-static.
+ if (isStaticSource() != isStaticTarget()) {
+ argumentIndex += isStaticSource() ? -1 : 1;
+ }
+ // Argument -1 is the receiver.
+ DexType targetArgumentType =
+ argumentIndex == -1
+ ? targetMethod.holder
+ : targetMethod.getParameters().values[argumentIndex];
+ if (sourceArgumentType != targetArgumentType
+ && targetArgumentType != appInfoForCastArguments.dexItemFactory().objectType) {
+ assert appInfoForCastArguments.isSubtype(targetArgumentType, sourceArgumentType);
+ instructions.add(new CfCheckCast(targetArgumentType));
+ }
+ }
+
+ private int getInvokeOpcode() {
+ switch (invokeType) {
+ case STATIC:
+ return Opcodes.INVOKESTATIC;
+ case VIRTUAL:
+ return Opcodes.INVOKEVIRTUAL;
+ case SPECIAL:
+ return Opcodes.INVOKESPECIAL;
+ }
+ throw new Unreachable("Unexpected invoke type: " + invokeType);
+ }
+
+ private DexType[] getSourceParameters() {
+ return sourceMethod.getParameters().values;
+ }
+
+ private boolean isSourceReturnVoid() {
+ return sourceMethod.getReturnType().isVoidType();
+ }
+
+ private ValueType getSourceReturnType() {
+ assert !isSourceReturnVoid();
+ return ValueType.fromDexType(sourceMethod.getReturnType());
+ }
+
+ private boolean isStaticSource() {
+ return staticSource;
+ }
+
+ private boolean isStaticTarget() {
+ return invokeType == InvokeType.STATIC;
+ }
+
+ private int sourceArguments() {
+ return sourceMethod.getParameters().size() + (isStaticSource() ? 0 : 1);
+ }
+
+ private int targetArguments() {
+ // A constructor delegate will allocate the instance so that is subtracted from args.
+ return targetMethod.getParameters().size()
+ + (isStaticTarget() || isConstructorDelegate ? 0 : 1);
+ }
+
+ private boolean validate() {
+ assert sourceMethod != null;
+ assert targetMethod != null;
+ assert invokeType != null;
+ assert isInterface != null;
+ assert sourceArguments() == targetArguments();
+ if (isConstructorDelegate) {
+ assert isStaticSource();
+ assert !sourceMethod.getReturnType().isVoidType();
+ assert targetMethod.getReturnType().isVoidType();
+ assert invokeType == InvokeType.SPECIAL;
+ } else if (castResult) {
+ assert ValueType.fromDexType(sourceMethod.getReturnType())
+ == ValueType.fromDexType(targetMethod.getReturnType());
+ } else {
+ assert sourceMethod.getReturnType() == targetMethod.getReturnType();
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index 06e3679..ae7fcf5 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -340,7 +340,7 @@
}
String name = namingLens.lookupName(field.field).toString();
String desc = namingLens.lookupDescriptor(field.field.type).toString();
- String signature = getSignature(field.annotations());
+ String signature = field.getFieldSignature().toRenamedString(namingLens, isTypeMissing);
Object value = getStaticValue(field);
FieldVisitor visitor = writer.visitField(access, name, desc, signature, value);
writeAnnotations(visitor::visitAnnotation, field.annotations().annotations);
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index 40713b8..e7ca5b5 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -21,7 +21,6 @@
import com.android.tools.r8.shaking.ProguardPackageNameList;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableMap;
@@ -40,7 +39,6 @@
private final ClassNamingStrategy classNamingStrategy;
private final PackageNamingStrategy packageNamingStrategy;
private final Iterable<? extends DexClass> classes;
- private final PackageObfuscationMode packageObfuscationMode;
private final boolean isAccessModificationAllowed;
private final Set<String> noObfuscationPrefixes = Sets.newHashSet();
private final Set<String> usedPackagePrefixes = Sets.newHashSet();
@@ -63,27 +61,16 @@
this.packageNamingStrategy = packageNamingStrategy;
this.classes = classes;
InternalOptions options = appView.options();
- this.packageObfuscationMode =
- options.testing.enableExperimentalRepackaging
- ? PackageObfuscationMode.NONE
- : options.getProguardConfiguration().getPackageObfuscationMode();
this.isAccessModificationAllowed =
options.getProguardConfiguration().isAccessModificationAllowed();
this.keepInnerClassStructure = options.keepInnerClassStructure();
// Initialize top-level naming state.
- if (options.testing.enableExperimentalRepackaging) {
- topLevelState = new Namespace("");
- String newPackageDescriptor =
- StringUtils.replaceAll(options.getProguardConfiguration().getPackagePrefix(), ".", "/");
- if (!newPackageDescriptor.isEmpty()) {
- registerPackagePrefixesAsUsed(newPackageDescriptor, false);
- }
- } else {
- topLevelState =
- new Namespace(
- getPackageBinaryNameFromJavaType(
- options.getProguardConfiguration().getPackagePrefix()));
+ topLevelState = new Namespace("");
+ String newPackageDescriptor =
+ StringUtils.replaceAll(options.getProguardConfiguration().getPackagePrefix(), ".", "/");
+ if (!newPackageDescriptor.isEmpty()) {
+ registerPackagePrefixesAsUsed(newPackageDescriptor, false);
}
states.put("", topLevelState);
@@ -284,25 +271,7 @@
if (noObfuscationPrefixes.contains(packageName) || keepPackageNames.matches(type)) {
return states.computeIfAbsent(packageName, Namespace::new);
}
- Namespace state = topLevelState;
- switch (packageObfuscationMode) {
- case NONE:
- // For general obfuscation, rename all the involved package prefixes.
- state = getStateForPackagePrefix(packageName);
- break;
- case REPACKAGE:
- // For repackaging, all classes are repackaged to a single package.
- state = topLevelState;
- break;
- case FLATTEN:
- // For flattening, all packages are repackaged to a single package.
- state = states.computeIfAbsent(packageName, k -> {
- String renamedPackagePrefix = topLevelState.nextPackagePrefix();
- return new Namespace(renamedPackagePrefix);
- });
- break;
- }
- return state;
+ return getStateForPackagePrefix(packageName);
}
private Namespace getStateForPackagePrefix(String prefix) {
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
index e1a49d8..1a51edf 100644
--- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
+++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
@@ -81,14 +81,8 @@
clazz.setClassSignature(genericSignatureTypeRewriter.rewrite(classSignature));
clazz.forEachField(
field ->
- field.setAnnotations(
- rewriteGenericSignatures(
- field.annotations(),
- genericSignatureParser::parseFieldSignature,
- genericSignatureCollector::getRenamedSignature,
- (signature, e) ->
- options.warningInvalidSignature(
- field, clazz.getOrigin(), signature, e))));
+ field.setFieldSignature(
+ genericSignatureTypeRewriter.rewrite(field.getFieldSignature())));
clazz.forEachMethod(
method ->
method.setAnnotations(
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
index 9e3cbce..3f625f2 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
@@ -45,6 +45,11 @@
}
@Override
+ public boolean hasCodeRewritings() {
+ return false;
+ }
+
+ @Override
protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
assert previous.getReboundReference() == null;
return FieldLookupResult.builder(this)
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index 4fa3212..fe2861a 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -207,6 +207,9 @@
private void processField(DexEncodedField field) {
field.setAnnotations(
field.annotations().rewrite(annotation -> rewriteAnnotation(field, annotation)));
+ if (!keep.signature) {
+ field.clearFieldSignature();
+ }
}
private DexAnnotation rewriteAnnotation(DexDefinition holder, DexAnnotation original) {
@@ -285,9 +288,6 @@
hasInnerClassesFromSet(clazz, classesToRetainInnerClassAttributeFor);
}
if (keptAnyway || keepForThisInnerClass || keepForThisEnclosingClass) {
- if (!keep.signature) {
- clazz.clearClassSignature();
- }
if (!keep.enclosingMethod) {
clazz.clearEnclosingMethodAttribute();
}
@@ -322,6 +322,9 @@
// reflection. (Note that clearing these attributes can enable more vertical class merging.)
clazz.clearEnclosingMethodAttribute();
clazz.clearInnerClasses();
+ }
+ // TODO(b/170077516): Prune attributes.
+ if (!keep.signature) {
clazz.clearClassSignature();
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index b0ee416..3b68071 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -921,7 +921,7 @@
return false;
}
return clazz
- .traverseProgramMethods(
+ .traverseProgramMembers(
member -> {
if (keepInfo.getInfo(member).isRepackagingAllowed(options())) {
return TraversalContinuation.CONTINUE;
diff --git a/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java b/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
index fc5c0b2..4786db4 100644
--- a/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.shaking;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import static com.android.tools.r8.graph.GenericSignature.NO_FIELD_TYPE_SIGNATURE;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
@@ -80,6 +81,7 @@
new DexEncodedField(
appView.dexItemFactory().createField(clazz.type, clinitField.type, clinitField.name),
accessFlags,
+ NO_FIELD_TYPE_SIGNATURE,
DexAnnotationSet.empty(),
null);
clazz.appendStaticField(encodedClinitField);
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index cd02bbb..d1200b4 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -2611,7 +2611,7 @@
// TODO(sgjesse): Does this have to be enqueued as a root item? Right now it is done as the
// marking for not renaming it is in the root set.
workList.enqueueMarkMethodKeptAction(valuesMethod, reason);
- keepInfo.keepMethod(valuesMethod);
+ keepInfo.joinMethod(valuesMethod, joiner -> joiner.pin().disallowMinification());
shouldNotBeMinified(valuesMethod.getReference());
}
}
@@ -2762,7 +2762,7 @@
if (!modifiers.allowsShrinking) {
// TODO(b/159589281): Evaluate this interpretation.
joiner.pin();
- if (!definition.getAccessFlags().isPublic()) {
+ if (definition.getAccessFlags().isPackagePrivateOrProtected()) {
joiner.requireAccessModificationForRepackaging();
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/ArrayUtils.java b/src/main/java/com/android/tools/r8/utils/ArrayUtils.java
index 4ce026a..b7839ea 100644
--- a/src/main/java/com/android/tools/r8/utils/ArrayUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ArrayUtils.java
@@ -5,6 +5,7 @@
import java.lang.reflect.Array;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.function.IntPredicate;
@@ -137,4 +138,18 @@
array[i] += array[i - 1];
}
}
+
+ /**
+ * Copies the current array to a new array that can fit one more element and adds 'element' to
+ * index |ts|. Only use this if adding a single element since copying the array is expensive.
+ *
+ * @param ts the original array
+ * @param element the element to add
+ * @return a new array with element on index |ts|
+ */
+ public static <T> T[] appendSingleElement(T[] ts, T element) {
+ T[] newArray = Arrays.copyOf(ts, ts.length + 1);
+ newArray[ts.length] = element;
+ return newArray;
+ }
}
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 091325a..a429fb1 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1253,7 +1253,6 @@
public boolean alwaysUsePessimisticRegisterAllocation = false;
public boolean enableCheckCastAndInstanceOfRemoval = true;
public boolean enableDeadSwitchCaseElimination = true;
- public boolean enableExperimentalRepackaging = false;
public boolean enableInvokeSuperToInvokeVirtualRewriting = true;
public boolean enableSwitchToIfRewriting = true;
public boolean enableEnumUnboxingDebugLogs = false;
diff --git a/src/test/examples/naming101/a/b/c.java b/src/test/examples/naming101/a/b/c.java
index b399bda..04dc965 100644
--- a/src/test/examples/naming101/a/b/c.java
+++ b/src/test/examples/naming101/a/b/c.java
@@ -4,5 +4,5 @@
package naming101.a.b;
public class c {
- static boolean flag = true;
+ public static boolean flag = true;
}
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index f5c75aa..67721e4 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -642,7 +642,7 @@
"530-checker-lse2",
TestCondition.match(
TestCondition.tools(DexTool.DX),
- TestCondition.compilers(CompilerUnderTest.D8),
+ TestCondition.compilers(CompilerUnderTest.R8, CompilerUnderTest.D8),
TestCondition.runtimesUpTo(DexVm.Version.V6_0_1)))
.put(
"534-checker-bce-deoptimization",
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index c888c1d..ddcb6c3 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -220,14 +220,6 @@
return self();
}
- public CR disableVerifer() {
- assert getBackend() == Backend.CF;
- if (!vmArguments.contains("-noverify")) {
- vmArguments.add("-noverify");
- }
- return self();
- }
-
public CR enableRuntimeAssertions() {
assert getBackend() == Backend.CF;
if (!vmArguments.contains("-ea")) {
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
index b3ff5a3..279004e 100644
--- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
+++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
@@ -210,6 +210,7 @@
@Test
public void testSignatures() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testParseSignaturesInJar(r8R8Release.getFirst());
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergingProducesFieldCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergingProducesFieldCollisionTest.java
new file mode 100644
index 0000000..d25d516
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergingProducesFieldCollisionTest.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2020, 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import org.junit.Test;
+
+public class MergingProducesFieldCollisionTest extends HorizontalClassMergingTestBase {
+ public MergingProducesFieldCollisionTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ byte[] transformedC = transformer(C.class).renameAndRemapField("v$1", "v").transform();
+
+ testForR8(parameters.getBackend())
+ .addKeepMainRule(Main.class)
+ .addProgramClassFileData(transformedC)
+ .addProgramClasses(Parent.class, A.class, B.class, Main.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccess()
+ .inspect(
+ codeInspector -> {
+ assertThat(codeInspector.clazz(Parent.class), isPresent());
+
+ ClassSubject aClassSubject = codeInspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ ClassSubject bClassSubject = codeInspector.clazz(B.class);
+ assertThat(bClassSubject, notIf(isPresent(), enableHorizontalClassMerging));
+
+ ClassSubject cClassSubject = codeInspector.clazz(C.class);
+ assertThat(cClassSubject, isPresent());
+
+ if (enableHorizontalClassMerging) {
+ assertEquals(
+ cClassSubject.allFields().get(0).type(),
+ cClassSubject.allFields().get(1).type());
+ }
+ });
+ }
+
+ @NoVerticalClassMerging
+ public static class Parent {
+ @NeverInline
+ public void foo() {
+ System.out.println("foo");
+ }
+ }
+
+ @NeverClassInline
+ public static class A extends Parent {
+ public A() {
+ System.out.println("b");
+ }
+ }
+
+ @NeverClassInline
+ public static class B extends Parent {
+ public B() {
+ System.out.println("b");
+ }
+
+ @NeverInline
+ public void foo() {
+ System.out.println("foo b");
+ }
+ }
+
+ @NeverClassInline
+ public static class C {
+ A v;
+ B v$1; // This field is renamed to v.
+
+ public C(A a, B b) {
+ v = a;
+ v$1 = b;
+ }
+
+ @NeverInline
+ public void foo() {
+ v.foo();
+ v$1.foo();
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ new C(new A(), new B()).foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
index ddff057..2519477 100644
--- a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
@@ -140,14 +140,8 @@
DexEncodedField field = yyInZZ.getField();
assertNotNull(field);
- fieldTypeSignature =
- GenericSignature.parseFieldTypeSignature(
- field.field.qualifiedName(),
- getGenericSignature(field, appView),
- Origin.unknown(),
- appView.dexItemFactory(),
- appView.options().reporter);
- assertNotNull(fieldTypeSignature);
+ fieldTypeSignature = field.getFieldSignature();
+ assertTrue(fieldTypeSignature.hasSignature());
// field type: A$Y$YY
assertTrue(fieldTypeSignature.isClassTypeSignature());
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/FieldSignatureTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/FieldSignatureTest.java
new file mode 100644
index 0000000..b0200fa
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/FieldSignatureTest.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2020, 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.graph.genericsignature;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.graph.GenericSignature.NO_FIELD_TYPE_SIGNATURE;
+import static com.google.common.base.Predicates.alwaysFalse;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.GenericSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignaturePrinter;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.Reporter;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class FieldSignatureTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public FieldSignatureTest(TestParameters parameters) {}
+
+ @Test
+ public void testClass() {
+ testParsingAndPrintingEqual("Lfoo/bar/baz;");
+ }
+
+ @Test
+ public void testMissingSemicolon() {
+ testParsingAndPrintingError("Lfoo/bar/baz")
+ .assertAllErrorsMatch(
+ diagnosticMessage(containsString("Invalid signature 'Lfoo/bar/baz;' for field A.")));
+ }
+
+ @Test
+ public void testClassWithEmptyTypeArguments() {
+ testParsingAndPrintingError("Lfoo/bar/baz<>;")
+ .assertAllErrorsMatch(
+ diagnosticMessage(containsString("Invalid signature 'Lfoo/bar/baz;' for field A.")));
+ }
+
+ @Test
+ public void testClassWithTypeVariableArguments() {
+ testParsingAndPrintingEqual("Lfoo/bar/baz<TT;>;");
+ }
+
+ @Test
+ public void testTypeVariable() {
+ testParsingAndPrintingEqual("TT;");
+ }
+
+ @Test
+ public void testPrimitive() {
+ testParsingAndPrintingError("I")
+ .assertAllErrorsMatch(
+ diagnosticMessage(containsString("Invalid signature 'I' for field A.")));
+ }
+
+ @Test
+ public void testArray() {
+ testParsingAndPrintingEqual("[I");
+ testParsingAndPrintingEqual("[Lfoo/bar/baz;");
+ }
+
+ @Test
+ public void testArrayWithGeneric() {
+ testParsingAndPrintingEqual("[Lfoo/bar/baz<[I+Lfoo/Qux<*>.Inner<-Lfoo/Quux<TT;>;>;>;");
+ }
+
+ private void testParsingAndPrintingEqual(String signature) {
+ FieldTypeSignature parsed =
+ GenericSignature.parseFieldTypeSignature(
+ "A", signature, Origin.unknown(), new DexItemFactory(), new Reporter());
+ GenericSignaturePrinter genericSignaturePrinter =
+ new GenericSignaturePrinter(NamingLens.getIdentityLens(), alwaysFalse());
+ genericSignaturePrinter.visitFieldTypeSignature(parsed);
+ String outSignature = genericSignaturePrinter.toString();
+ assertEquals(signature, outSignature);
+ }
+
+ private TestDiagnosticMessages testParsingAndPrintingError(String signature) {
+ TestDiagnosticMessagesImpl testDiagnosticMessages = new TestDiagnosticMessagesImpl();
+ FieldTypeSignature parsed =
+ GenericSignature.parseFieldTypeSignature(
+ "A",
+ signature,
+ Origin.unknown(),
+ new DexItemFactory(),
+ new Reporter(testDiagnosticMessages));
+ assertEquals(NO_FIELD_TYPE_SIGNATURE, parsed);
+ return testDiagnosticMessages;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/PruneNotNullBranchD8Test.java b/src/test/java/com/android/tools/r8/ir/optimize/PruneNotNullBranchD8Test.java
new file mode 100644
index 0000000..870787f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/PruneNotNullBranchD8Test.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2020, 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.optimize;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.io.File;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class PruneNotNullBranchD8Test extends TestBase {
+
+ private final TestParameters parameters;
+ private final String EXPECTED = "foo_bar_baz0";
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ public PruneNotNullBranchD8Test(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ testForD8(parameters.getBackend())
+ .addProgramClasses(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED)
+ .inspect(
+ inspector -> {
+ ClassSubject main = inspector.clazz(Main.class);
+ assertThat(main, isPresent());
+ MethodSubject mainMethod = main.mainMethod();
+ assertThat(mainMethod, isPresent());
+ // TODO(b/170060113): We should be able to remove the throw.
+ assertTrue(mainMethod.streamInstructions().anyMatch(InstructionSubject::isThrow));
+ });
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ File file;
+ if (args.length == 0) {
+ file = new File("foo_bar_baz0");
+ } else {
+ file = new File("foo_bar_baz1");
+ }
+ if (file != null) {
+ System.out.println(file.getPath());
+ return;
+ }
+ throw new RuntimeException("Will always be non-zero");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/MinifierFieldSignatureTest.java b/src/test/java/com/android/tools/r8/naming/MinifierFieldSignatureTest.java
index 12b18e4..53e52b4 100644
--- a/src/test/java/com/android/tools/r8/naming/MinifierFieldSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/naming/MinifierFieldSignatureTest.java
@@ -4,8 +4,11 @@
package com.android.tools.r8.naming;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -21,16 +24,16 @@
import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Opcodes.V1_8;
-import com.android.tools.r8.DiagnosticsChecker;
-import com.android.tools.r8.R8Command;
-import com.android.tools.r8.StringConsumer;
+import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FieldSubject;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
import java.util.function.Consumer;
@@ -44,6 +47,18 @@
@RunWith(Parameterized.class)
public class MinifierFieldSignatureTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public MinifierFieldSignatureTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
/*
class Fields<X extends String> {
@@ -61,16 +76,6 @@
private String anArrayOfXSignature = "[TX;";
private String aFieldsOfXSignature = "LFields<TX;>;";
private String aFieldsOfXInnerSignature = "LFields<TX;>.Inner;";
- private Backend backend;
-
- @Parameters(name = "Backend: {0}")
- public static Backend[] data() {
- return ToolHelper.getBackends();
- }
-
- public MinifierFieldSignatureTest(Backend backend) {
- this.backend = backend;
- }
public byte[] dumpFields(Map<String, String> signatures) throws Exception {
@@ -172,25 +177,26 @@
public void runTest(
ImmutableMap<String, String> signatures,
- Consumer<DiagnosticsChecker> diagnostics,
+ Consumer<TestDiagnosticMessages> diagnostics,
Consumer<CodeInspector> inspect)
throws Exception {
- DiagnosticsChecker checker = new DiagnosticsChecker();
- CodeInspector inspector =
- new CodeInspector(
- ToolHelper.runR8(
- R8Command.builder(checker)
- .addClassProgramData(dumpFields(signatures), Origin.unknown())
- .addClassProgramData(dumpInner(), Origin.unknown())
- .addProguardConfiguration(
- ImmutableList.of(
- "-keepattributes InnerClasses,EnclosingMethod,Signature",
- "-keep,allowobfuscation class ** { *; }"),
- Origin.unknown())
- .setProgramConsumer(emptyConsumer(backend))
- .addLibraryFiles(runtimeJar(backend))
- .setProguardMapConsumer(StringConsumer.emptyConsumer())
- .build()));
+ R8TestCompileResult compileResult =
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(dumpFields(signatures), dumpInner())
+ .addKeepAttributes(
+ ProguardKeepAttributes.INNER_CLASSES,
+ ProguardKeepAttributes.ENCLOSING_METHOD,
+ ProguardKeepAttributes.SIGNATURE)
+ .addKeepAllClassesRuleWithAllowObfuscation()
+ .setMinApi(parameters.getApiLevel())
+ .allowDiagnosticMessages()
+ .addOptionsModification(
+ internalOptions ->
+ internalOptions.testing.disableMappingToOriginalProgramVerification = true)
+ .compile();
+
+ CodeInspector inspector = compileResult.inspector();
+
// All classes are kept, and renamed.
ClassSubject clazz = inspector.clazz("Fields");
assertThat(clazz, isPresentAndRenamed());
@@ -225,13 +231,18 @@
assertEquals(
aFieldsOfXInnerSignature, aFieldsOfXInner.getOriginalSignatureAttribute());
}
-
- diagnostics.accept(checker);
inspect.accept(inspector);
+
+ TestDiagnosticMessages diagnosticMessages = compileResult.getDiagnosticMessages();
+ diagnosticMessages.assertNoInfos();
+ diagnosticMessages.assertNoErrors();
+ diagnostics.accept(diagnosticMessages);
}
- private void testSingleField(String name, String signature,
- Consumer<DiagnosticsChecker> diagnostics,
+ private void testSingleField(
+ String name,
+ String signature,
+ Consumer<TestDiagnosticMessages> diagnostics,
Consumer<CodeInspector> inspector)
throws Exception {
ImmutableMap<String, String> signatures = ImmutableMap.of(name, signature);
@@ -242,11 +253,8 @@
assertSame(Origin.unknown(), origin);
}
- private void noWarnings(DiagnosticsChecker checker) {
- assertEquals(0, checker.warnings.size());
- }
-
private void noInspection(CodeInspector inspector) {
+ // Intentionally left empty.
}
private void noSignatureAttribute(FieldSubject field) {
@@ -258,48 +266,61 @@
@Test
public void originalJavacSignatures() throws Exception {
// Test using the signatures generated by javac.
- runTest(ImmutableMap.of(), this::noWarnings, this::noInspection);
+ runTest(ImmutableMap.of(), TestDiagnosticMessages::assertNoWarnings, this::noInspection);
}
@Test
public void signatureEmpty() throws Exception {
- testSingleField("anX", "", this::noWarnings,
+ testSingleField(
+ "anX",
+ "",
+ TestDiagnosticMessages::assertNoWarnings,
inspector -> noSignatureAttribute(lookupAnX(inspector)));
}
@Test
public void signatureInvalid() throws Exception {
- testSingleField("anX", "X", diagnostics -> {
- assertEquals(1, diagnostics.warnings.size());
- // TODO(sgjesse): The position 2 reported here is one off.
- DiagnosticsChecker.checkDiagnostic(diagnostics.warnings.get(0), this::isOriginUnknown,
- "Invalid signature 'X' for field",
- "java.lang.String Fields.anX",
- "Expected L, [ or T at position 2");
- }, inspector -> noSignatureAttribute(lookupAnX(inspector)));
+ testSingleField(
+ "anX",
+ "X",
+ diagnostics -> {
+ diagnostics.assertWarningsMatch(
+ diagnosticMessage(
+ allOf(
+ containsString("Invalid signature 'X' for field anX"),
+ // TODO(sgjesse): The position 2 reported here is one off.
+ containsString("Expected L, [ or T at position 2"))));
+ },
+ inspector -> noSignatureAttribute(lookupAnX(inspector)));
}
@Test
public void classNotFound() throws Exception {
String signature = "LNotFound<TX;>.InnerNotFound.InnerAlsoNotFound;";
- testSingleField("anX", signature, this::noWarnings, inspector -> {
- assertThat(inspector.clazz("NotFound"), not(isPresent()));
- assertEquals(signature, lookupAnX(inspector).getOriginalSignatureAttribute());
- });
+ testSingleField(
+ "anX",
+ signature,
+ TestDiagnosticMessages::assertNoWarnings,
+ inspector -> {
+ assertThat(inspector.clazz("NotFound"), not(isPresent()));
+ assertEquals(signature, lookupAnX(inspector).getOriginalSignatureAttribute());
+ });
}
@Test
public void multipleWarnings() throws Exception {
- runTest(ImmutableMap.of(
- "anX", "X",
- "anArrayOfX", "X",
- "aFieldsOfX", "X"
- ), diagnostics -> {
- assertEquals(3, diagnostics.warnings.size());
- }, inspector -> {
- noSignatureAttribute(lookupAnX(inspector));
- noSignatureAttribute(lookupAnArrayOfX(inspector));
- noSignatureAttribute(lookupAFieldsOfX(inspector));
- });
+ runTest(
+ ImmutableMap.of(
+ "anX", "X",
+ "anArrayOfX", "X",
+ "aFieldsOfX", "X"),
+ diagnostics -> {
+ diagnostics.assertWarningsCount(3);
+ },
+ inspector -> {
+ noSignatureAttribute(lookupAnX(inspector));
+ noSignatureAttribute(lookupAnArrayOfX(inspector));
+ noSignatureAttribute(lookupAFieldsOfX(inspector));
+ });
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingSignatureTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingSignatureTest.java
index 2980f0e..40ba160 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingSignatureTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.FieldSubject;
import java.io.IOException;
@@ -36,7 +37,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private TestParameters parameters;
@@ -52,9 +53,12 @@
.addProgramClasses(A.class, SignatureTest.class)
.addKeepClassRulesWithAllowObfuscation(A.class)
.addKeepClassAndMembersRules(SignatureTest.class)
- .addKeepAttributes("Signature", "InnerClasses", "EnclosingMethod")
+ .addKeepAttributes(
+ ProguardKeepAttributes.SIGNATURE,
+ ProguardKeepAttributes.INNER_CLASSES,
+ ProguardKeepAttributes.ENCLOSING_METHOD)
.addApplyMapping(A.class.getTypeName() + " -> foo:")
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), SignatureTest.class)
.assertSuccessWithOutput("HELLO")
.inspect(
@@ -63,7 +67,7 @@
assertThat(clazz, isPresent());
FieldSubject field = clazz.uniqueFieldWithName("field");
assertThat(field, isPresent());
- assertThat(field.getSignatureAnnotationValue(), containsString("foo"));
+ assertThat(field.getFinalSignatureAttribute(), containsString("foo"));
});
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java b/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
index 18d0719..8757685 100644
--- a/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
+++ b/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
@@ -14,9 +14,11 @@
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.graph.DexAnnotationElement;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueArray;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
@@ -25,20 +27,26 @@
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class B124357885Test extends TestBase {
- public final boolean minification;
- @Parameterized.Parameters(name = "Minification: {0}")
- public static Boolean[] data() {
- return BooleanUtils.values();
+ private final TestParameters parameters;
+ private final boolean minification;
+
+ @Parameters(name = "{0}, minification: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
}
- public B124357885Test(boolean minification) {
+ public B124357885Test(TestParameters parameters, boolean minification) {
+ this.parameters = parameters;
this.minification = minification;
}
@@ -69,11 +77,15 @@
@Test
public void test() throws Exception {
R8TestCompileResult compileResult =
- testForR8(Backend.DEX)
+ testForR8(parameters.getBackend())
.addProgramClasses(Main.class, Service.class, Foo.class, FooImpl.class)
.addKeepMainRule(Main.class)
- .addKeepRules("-keepattributes Signature,InnerClasses,EnclosingMethod")
+ .addKeepAttributes(
+ ProguardKeepAttributes.SIGNATURE,
+ ProguardKeepAttributes.INNER_CLASSES,
+ ProguardKeepAttributes.ENCLOSING_METHOD)
.minification(minification)
+ .setMinApi(parameters.getApiLevel())
.compile()
.inspect(
inspector -> {
@@ -88,31 +100,31 @@
checkSignatureAnnotation(inspector, signature);
});
- String fooImplFinalName = compileResult.inspector().clazz(FooImpl.class).getFinalName();
+ String fooImplFinalName = compileResult.inspector().clazz(FooImpl.class).getFinalName();
- compileResult
- .run(Main.class)
- .assertSuccessWithOutput(StringUtils.lines(fooImplFinalName, fooImplFinalName));
+ compileResult
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutput(StringUtils.lines(fooImplFinalName, fooImplFinalName));
}
-}
-class Main {
- public static void main(String... args) throws Exception {
- Method method = Service.class.getMethod("fooList");
- ParameterizedType type = (ParameterizedType) method.getGenericReturnType();
- Class<?> rawType = (Class<?>) type.getRawType();
- System.out.println(rawType.getName());
+ public static class Main {
+ public static void main(String... args) throws Exception {
+ Method method = Service.class.getMethod("fooList");
+ ParameterizedType type = (ParameterizedType) method.getGenericReturnType();
+ Class<?> rawType = (Class<?>) type.getRawType();
+ System.out.println(rawType.getName());
- // Convince R8 we only use subtypes to get class merging of Foo into FooImpl.
- Foo<String> foo = new FooImpl<>();
- System.out.println(foo.getClass().getCanonicalName());
+ // Convince R8 we only use subtypes to get class merging of Foo into FooImpl.
+ Foo<String> foo = new FooImpl<>();
+ System.out.println(foo.getClass().getCanonicalName());
+ }
}
+
+ interface Service {
+ Foo<String> fooList();
+ }
+
+ interface Foo<T> {}
+
+ public static class FooImpl<T> implements Foo<T> {}
}
-
-interface Service {
- Foo<String> fooList();
-}
-
-interface Foo<T> {}
-
-class FooImpl<T> implements Foo<T> {}
diff --git a/src/test/java/com/android/tools/r8/naming/b126592786/B126592786.java b/src/test/java/com/android/tools/r8/naming/b126592786/B126592786.java
index 78f0d0c..de81c15 100644
--- a/src/test/java/com/android/tools/r8/naming/b126592786/B126592786.java
+++ b/src/test/java/com/android/tools/r8/naming/b126592786/B126592786.java
@@ -57,7 +57,7 @@
.compile()
.inspect(
inspector -> {
- String genericTypeDescriptor = "Ljava/lang/Object;";
+ String genericTypeDescriptor = "*";
if (genericTypeLive) {
ClassSubject genericType = inspector.clazz(GenericType.class);
assertThat(genericType, isPresentAndRenamed(minify));
@@ -66,8 +66,7 @@
String expectedSignature = "Ljava/util/List<" + genericTypeDescriptor + ">;";
FieldSubject list = inspector.clazz(A.class).uniqueFieldWithName("list");
assertThat(list, isPresent());
- assertThat(list.getSignatureAnnotation(), isPresent());
- assertEquals(expectedSignature, list.getSignatureAnnotationValue());
+ assertEquals(expectedSignature, list.getFinalSignatureAttribute());
})
.run(mainClass)
.assertSuccess();
diff --git a/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java b/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java
index a29c664..ddf20ef 100644
--- a/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java
+++ b/src/test/java/com/android/tools/r8/naming/b72391662/B72391662.java
@@ -140,7 +140,7 @@
assertThat(testClass, isPresent());
// Test the totally unused method.
- MethodSubject staticMethod = testClass.method("void", "unused", ImmutableList.of());
+ MethodSubject staticMethod = testClass.uniqueMethodWithName("unused");
assertThat(staticMethod, isPresent());
assertEquals(minify, staticMethod.isRenamed());
if (shrinker.isR8()) {
@@ -150,7 +150,7 @@
}
// Test an indirectly referred method.
- staticMethod = testClass.method("java.lang.String", "staticMethod", ImmutableList.of());
+ staticMethod = testClass.uniqueMethodWithName("staticMethod");
assertThat(staticMethod, isPresent());
assertEquals(minify, staticMethod.isRenamed());
boolean publicizeCondition = shrinker.isR8() ? allowAccessModification
@@ -191,7 +191,7 @@
assertThat(testClass, isPresent());
// Test the totally unused method.
- MethodSubject staticMethod = testClass.method("void", "unused", ImmutableList.of());
+ MethodSubject staticMethod = testClass.uniqueMethodWithName("unused");
assertThat(staticMethod, isPresent());
assertEquals(minify, staticMethod.isRenamed());
if (shrinker.isR8()) {
@@ -201,7 +201,7 @@
}
// Test an indirectly referred method.
- staticMethod = testClass.method("java.lang.String", "staticMethod", ImmutableList.of());
+ staticMethod = testClass.uniqueMethodWithName("staticMethod");
assertThat(staticMethod, isPresent());
assertEquals(minify, staticMethod.isRenamed());
boolean publicizeCondition = shrinker.isR8() ? allowAccessModification
@@ -254,11 +254,11 @@
assertThat(testClass, isPresent());
// Test the totally unused method.
- MethodSubject staticMethod = testClass.method("void", "unused", ImmutableList.of());
+ MethodSubject staticMethod = testClass.uniqueMethodWithName("unused");
assertThat(staticMethod, not(isPresent()));
// Test an indirectly referred method.
- staticMethod = testClass.method("java.lang.String", "staticMethod", ImmutableList.of());
+ staticMethod = testClass.uniqueMethodWithName("staticMethod");
assertThat(staticMethod, isPresent());
assertEquals(minify, staticMethod.isRenamed());
boolean publicizeCondition = shrinker.isR8() ? allowAccessModification
diff --git a/src/test/java/com/android/tools/r8/proguard/configuration/RepackagingCompatibilityTest.java b/src/test/java/com/android/tools/r8/proguard/configuration/RepackagingCompatibilityTest.java
index 8d62a04..b078b2f 100644
--- a/src/test/java/com/android/tools/r8/proguard/configuration/RepackagingCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/proguard/configuration/RepackagingCompatibilityTest.java
@@ -62,7 +62,7 @@
@Test
public void testProguard() throws Exception {
- runTest(testForProguard(), "Proguard");
+ runTest(testForProguard().addKeepRules("-dontwarn " + getClass().getTypeName()), "Proguard");
}
private void runTest(TestShrinkerBuilder<?, ?, ?, ?, ?> builder, String shrinker)
@@ -125,11 +125,11 @@
throw new Unreachable();
}
}
-}
-class RepackagingCompatabilityTestClass {
+ public static class RepackagingCompatabilityTestClass {
- public static void main(String[] args) {
- System.out.println("Hello world!");
+ public static void main(String[] args) {
+ System.out.println("Hello world!");
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageTest.java
index b9d3c40..150a7cc 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageTest.java
@@ -66,28 +66,25 @@
private final boolean allowAccessModification;
- @Parameters(name = "{3}, allow access modification: {0}, experimental: {1}, kind: {2}")
+ @Parameters(name = "{2}, allow access modification: {0}, kind: {1}")
public static List<Object[]> data() {
return buildParameters(
BooleanUtils.values(),
- BooleanUtils.values(),
ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
getTestParameters().withAllRuntimesAndApiLevels().build());
}
public RepackageTest(
boolean allowAccessModification,
- boolean enableExperimentalRepackaging,
String flattenPackageHierarchyOrRepackageClasses,
TestParameters parameters) {
- super(enableExperimentalRepackaging, flattenPackageHierarchyOrRepackageClasses, parameters);
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
this.allowAccessModification = allowAccessModification;
}
@Test
public void testJvm() throws Exception {
assumeFalse(allowAccessModification);
- assumeFalse(isExperimentalRepackaging());
assumeTrue(isFlattenPackageHierarchy());
assumeTrue(parameters.isCfRuntime());
testForJvm()
@@ -134,11 +131,7 @@
* eligible for repackaging (or it needs to stay in its original package).
*/
private void forEachClass(BiConsumer<Class<?>, Boolean> consumer) {
- // TODO(b/165783399): This should be renamed to markAlwaysEligible() and always pass `true` to
- // the consumer, since these classes should be repackaged independent of
- // -allowaccessmodification.
- Consumer<Class<?>> markShouldAlwaysBeEligible =
- clazz -> consumer.accept(clazz, allowAccessModification || isExperimentalRepackaging());
+ Consumer<Class<?>> markShouldAlwaysBeEligible = clazz -> consumer.accept(clazz, true);
Consumer<Class<?>> markEligibleWithAllowAccessModification =
clazz -> consumer.accept(clazz, allowAccessModification);
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageTestBase.java b/src/test/java/com/android/tools/r8/repackage/RepackageTestBase.java
index 3e2f695..9a1ad72 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageTestBase.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageTestBase.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
-import static org.junit.Assert.assertFalse;
import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestBase;
@@ -24,7 +23,6 @@
public abstract class RepackageTestBase extends TestBase {
- private final boolean enableExperimentalRepackaging;
private final String flattenPackageHierarchyOrRepackageClasses;
protected final TestParameters parameters;
@@ -36,15 +34,8 @@
}
public RepackageTestBase(
- String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
- this(true, flattenPackageHierarchyOrRepackageClasses, parameters);
- }
-
- public RepackageTestBase(
- boolean enableExperimentalRepackaging,
String flattenPackageHierarchyOrRepackageClasses,
TestParameters parameters) {
- this.enableExperimentalRepackaging = enableExperimentalRepackaging;
this.flattenPackageHierarchyOrRepackageClasses = flattenPackageHierarchyOrRepackageClasses;
this.parameters = parameters;
}
@@ -140,18 +131,8 @@
}
protected void configureRepackaging(R8TestBuilder<?> testBuilder) {
- testBuilder
- .addKeepRules(
- "-" + flattenPackageHierarchyOrRepackageClasses + " \"" + getRepackagePackage() + "\"")
- .addOptionsModification(
- options -> {
- assertFalse(options.testing.enableExperimentalRepackaging);
- options.testing.enableExperimentalRepackaging = enableExperimentalRepackaging;
- });
- }
-
- protected boolean isExperimentalRepackaging() {
- return enableExperimentalRepackaging;
+ testBuilder.addKeepRules(
+ "-" + flattenPackageHierarchyOrRepackageClasses + " \"" + getRepackagePackage() + "\"");
}
protected boolean isFlattenPackageHierarchy() {
diff --git a/src/test/java/com/android/tools/r8/shaking/attributes/ClassSignaturesTest.java b/src/test/java/com/android/tools/r8/shaking/attributes/ClassSignaturesTest.java
new file mode 100644
index 0000000..7ca532d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/attributes/ClassSignaturesTest.java
@@ -0,0 +1,98 @@
+// Copyright (c) 2020, 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.shaking.attributes;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.util.Iterator;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ClassSignaturesTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final String EXPECTED = "Hello World!";
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public ClassSignaturesTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(A.class, Main.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED)
+ .inspect(this::inspect);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(A.class, Main.class)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .addKeepAttributes(
+ ProguardKeepAttributes.SIGNATURE,
+ ProguardKeepAttributes.INNER_CLASSES,
+ ProguardKeepAttributes.ENCLOSING_METHOD)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED)
+ .inspect(this::inspect);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject aClass = inspector.clazz(A.class);
+ assertThat(aClass, isPresent());
+ assertEquals(
+ "Ljava/lang/Object;Ljava/lang/Iterable<"
+ + "Lcom/android/tools/r8/shaking/attributes/ClassSignaturesTest$Main;>;",
+ aClass.getFinalSignatureAttribute());
+ }
+
+ @NeverClassInline
+ public static class A implements Iterable<Main> {
+
+ @Override
+ @NeverInline
+ public Iterator<Main> iterator() {
+ throw new RuntimeException("FooBar");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ try {
+ new A().iterator();
+ } catch (RuntimeException e) {
+ if (!e.getMessage().contains("FooBar")) {
+ throw e;
+ }
+ System.out.println("Hello World!");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/enums/EnumSafeGuardPruneTestRunner.java b/src/test/java/com/android/tools/r8/shaking/enums/EnumSafeGuardPruneTestRunner.java
new file mode 100644
index 0000000..b40ce28
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/enums/EnumSafeGuardPruneTestRunner.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2020, 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.shaking.enums;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class EnumSafeGuardPruneTestRunner extends TestBase {
+
+ private final TestParameters parameters;
+ private final String EXPECTED = "SOUTH";
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public EnumSafeGuardPruneTestRunner(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addInnerClasses(EnumSafeGuardPruneTestRunner.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED)
+ .inspect(this::inspect);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(EnumSafeGuardPruneTestRunner.class)
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED)
+ .inspect(this::inspect);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject main = inspector.clazz(Main.class);
+ assertThat(main, isPresent());
+ MethodSubject mainMethod = main.mainMethod();
+ assertThat(mainMethod, isPresent());
+ boolean hasRemoveThisBranch =
+ mainMethod
+ .streamInstructions()
+ .anyMatch(instr -> instr.isConstString("Remove this branch", JumboStringMode.DISALLOW));
+ // TODO(b/170084314): Consider removing this.
+ assertTrue(hasRemoveThisBranch);
+ }
+
+ public enum Corners {
+ NORTH,
+ SOUTH,
+ WEST
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ Corners corners =
+ args.length == 0 ? Corners.SOUTH : (args.length == 1 ? Corners.NORTH : Corners.WEST);
+ switch (corners) {
+ case NORTH:
+ System.out.println("NORTH");
+ return;
+ case SOUTH:
+ System.out.println("SOUTH");
+ return;
+ case WEST:
+ System.out.println("WEST");
+ return;
+ default:
+ throw new RuntimeException("Remove this branch");
+ }
+ }
+ }
+}
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 4779b8e..d9f3b1d 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -474,6 +474,11 @@
boolean test(int access, String name, String descriptor, String signature, Object value);
}
+ @FunctionalInterface
+ public interface FieldSignaturePredicate {
+ boolean test(String name, String typeDescriptor);
+ }
+
public ClassFileTransformer removeInnerClasses() {
return addClassTransformer(
new ClassTransformer() {
@@ -522,6 +527,42 @@
});
}
+ public ClassFileTransformer remapField(FieldSignaturePredicate predicate, String newName) {
+ return addMethodTransformer(
+ new MethodTransformer() {
+ @Override
+ public void visitFieldInsn(
+ final int opcode, final String owner, final String name, final String descriptor) {
+ if (predicate.test(name, descriptor)) {
+ super.visitFieldInsn(opcode, owner, newName, descriptor);
+ } else {
+ super.visitFieldInsn(opcode, owner, name, descriptor);
+ }
+ }
+ });
+ }
+
+ public ClassFileTransformer renameField(FieldSignaturePredicate predicate, String newName) {
+ return addClassTransformer(
+ new ClassTransformer() {
+ @Override
+ public FieldVisitor visitField(
+ int access, String name, String descriptor, String signature, Object value) {
+ if (predicate.test(name, descriptor)) {
+ return super.visitField(access, newName, descriptor, signature, value);
+ } else {
+ return super.visitField(access, name, descriptor, signature, value);
+ }
+ }
+ });
+ }
+
+ public ClassFileTransformer renameAndRemapField(String oldName, String newName) {
+ FieldSignaturePredicate matchPredicate = (name, signature) -> oldName.equals(name);
+ remapField(matchPredicate, newName);
+ return renameField(matchPredicate, newName);
+ }
+
/** Abstraction of the MethodVisitor.visitMethodInsn method with its sub visitor. */
@FunctionalInterface
public interface MethodInsnTransform {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
index 9ef7938..fce43c7 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
@@ -134,13 +134,12 @@
@Override
public String getOriginalSignatureAttribute() {
return codeInspector.getOriginalSignatureAttribute(
- codeInspector.getFinalSignatureAttribute(dexField.annotations()),
- GenericSignatureParser::parseFieldSignature);
+ dexField.getFieldSignature().toString(), GenericSignatureParser::parseFieldSignature);
}
@Override
public String getFinalSignatureAttribute() {
- return codeInspector.getFinalSignatureAttribute(dexField.annotations());
+ return dexField.getFieldSignature().toString();
}
@Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java
index e2561b0..77172c3 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java
@@ -4,9 +4,6 @@
package com.android.tools.r8.utils.codeinspector;
-import com.android.tools.r8.graph.DexAnnotationElement;
-import com.android.tools.r8.graph.DexValue;
-import com.android.tools.r8.graph.DexValue.DexValueArray;
import com.android.tools.r8.naming.MemberNaming.Signature;
public abstract class MemberSubject extends Subject {
@@ -53,32 +50,6 @@
public abstract AnnotationSubject annotation(String name);
- public AnnotationSubject getSignatureAnnotation() {
- return annotation("dalvik.annotation.Signature");
- }
-
- public String getSignatureAnnotationValue() {
- AnnotationSubject annotation = getSignatureAnnotation();
- if (!annotation.isPresent()) {
- return null;
- }
-
- assert annotation.getAnnotation().elements.length == 1;
- DexAnnotationElement element = annotation.getAnnotation().elements[0];
- assert element.name.toString().equals("value");
- assert element.value.isDexValueArray();
- DexValueArray array = element.value.asDexValueArray();
- StringBuilder builder = new StringBuilder();
- for (DexValue value : array.getValues()) {
- if (value.isDexValueString()) {
- builder.append(value.asDexValueString().value);
- } else {
- builder.append(value.toString());
- }
- }
- return builder.toString();
- }
-
public FieldSubject asFieldSubject() {
return null;
}