Merge commit 'a30e76e94a2d4da54ab602eac12224e945afe251' into dev-release
diff --git a/build.gradle b/build.gradle
index 99676ef..54f0898 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1975,6 +1975,10 @@
include "com/android/tools/r8/internal/**"
}
+ if (project.hasProperty('test_namespace')) {
+ include "com/android/tools/r8/" + project.getProperty('test_namespace') + "/**"
+ }
+
if (project.hasProperty('tool')) {
if (project.property('tool') == 'r8') {
exclude "com/android/tools/r8/jctf/**"
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
index 586c4f1..18b86c7 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommandParser.java
@@ -36,6 +36,13 @@
" # is the default handling of javac assertion code when",
" # generating class file format.");
+ static final Iterable<String> THREAD_COUNT_USAGE_MESSAGE =
+ Arrays.asList(
+ " " + THREAD_COUNT_FLAG + " <number of threads>",
+ " # Number of threads to use for compilation. If not specified",
+ " # the number will be based on heuristics taking the number",
+ " # of cores into account.");
+
public static void parsePositiveIntArgument(
Consumer<Diagnostic> errorConsumer,
String flag,
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
index 93d9058..2ed9890 100644
--- a/src/main/java/com/android/tools/r8/D8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -143,6 +143,7 @@
" --main-dex-list-output <file>",
" # Output resulting main dex list in <file>."),
ASSERTIONS_USAGE_MESSAGE,
+ THREAD_COUNT_USAGE_MESSAGE,
Arrays.asList(
" --version # Print the version of d8.",
" --help # Print this message.")));
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
index 17b7485..c295175 100644
--- a/src/main/java/com/android/tools/r8/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.LazyLoadedDexApplication;
import com.android.tools.r8.graph.ParameterAnnotationsList;
@@ -173,6 +174,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
+ ClassSignature.NO_CLASS_SIGNATURE,
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
diff --git a/src/main/java/com/android/tools/r8/L8CommandParser.java b/src/main/java/com/android/tools/r8/L8CommandParser.java
index 5d9392c..e816320 100644
--- a/src/main/java/com/android/tools/r8/L8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/L8CommandParser.java
@@ -54,6 +54,7 @@
" # <file> is a desugared library configuration"
+ " (json)."),
ASSERTIONS_USAGE_MESSAGE,
+ THREAD_COUNT_USAGE_MESSAGE,
Arrays.asList(
" --version # Print the version of l8.",
" --help # Print this message.")));
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 92d9c12..9bee3f3 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -71,12 +71,12 @@
import com.android.tools.r8.naming.SeedMapper;
import com.android.tools.r8.naming.SourceFileRewriter;
import com.android.tools.r8.naming.signature.GenericSignatureRewriter;
-import com.android.tools.r8.optimize.BridgeHoisting;
import com.android.tools.r8.optimize.ClassAndMemberPublicizer;
import com.android.tools.r8.optimize.MemberRebindingAnalysis;
import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
import com.android.tools.r8.optimize.MemberRebindingIdentityLensFactory;
import com.android.tools.r8.optimize.VisibilityBridgeRemover;
+import com.android.tools.r8.optimize.bridgehoisting.BridgeHoisting;
import com.android.tools.r8.origin.CommandLineOrigin;
import com.android.tools.r8.repackaging.Repackaging;
import com.android.tools.r8.repackaging.RepackagingLens;
@@ -703,7 +703,7 @@
timing)
.withEnumValueInfoMaps(enumValueInfoMapCollection));
// Rerunning the enqueuer should not give rise to any method rewritings.
- assert enqueuer.buildGraphLens(appView) == null;
+ assert enqueuer.buildGraphLens() == null;
appView.withGeneratedMessageLiteBuilderShrinker(
shrinker ->
shrinker.rewriteDeadBuilderReferencesFromDynamicMethods(
@@ -892,12 +892,14 @@
|| options.getProguardConfiguration().hasApplyMappingFile()) {
assert appView.rootSet().verifyKeptItemsAreKept(appView);
}
- assert appView
- .graphLens()
- .verifyMappingToOriginalProgram(
- appView,
- new ApplicationReader(inputApp.withoutMainDexList(), options, timing)
- .read(executorService));
+
+ assert options.testing.disableMappingToOriginalProgramVerification
+ || appView
+ .graphLens()
+ .verifyMappingToOriginalProgram(
+ appView,
+ new ApplicationReader(inputApp.withoutMainDexList(), options, timing)
+ .read(executorService));
// Report synthetic rules (only for testing).
// TODO(b/120959039): Move this to being reported through the graph consumer.
@@ -1019,7 +1021,7 @@
options.getProguardConfiguration().getDontWarnPatterns(),
executorService,
timing));
- NestedGraphLens lens = enqueuer.buildGraphLens(appView);
+ NestedGraphLens lens = enqueuer.buildGraphLens();
appView.rewriteWithLens(lens);
if (InternalOptions.assertionsEnabled()) {
// Register the dead proto types. These are needed to verify that no new missing types are
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
index 8c93579..b796705 100644
--- a/src/main/java/com/android/tools/r8/R8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -99,6 +99,7 @@
" --main-dex-list-output <file> ",
" # Output the full main-dex list in <file>."),
ASSERTIONS_USAGE_MESSAGE,
+ THREAD_COUNT_USAGE_MESSAGE,
Arrays.asList(
" --version # Print the version of r8.",
" --help # Print this message.")));
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 b0f836c..6073b28 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -48,6 +48,7 @@
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.PredicateUtils;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ThreadUtils;
@@ -66,6 +67,7 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
public class ApplicationWriter {
@@ -76,6 +78,7 @@
public final NamingLens namingLens;
public final InternalOptions options;
private final CodeToKeep desugaredLibraryCodeToKeep;
+ private final Predicate<DexType> isTypeMissing;
public List<Marker> markers;
public List<DexString> markerStrings;
@@ -179,6 +182,8 @@
this.namingLens = namingLens;
this.proguardMapSupplier = proguardMapSupplier;
this.programConsumer = consumer;
+ this.isTypeMissing =
+ PredicateUtils.isNull(appView.appInfo()::definitionForWithoutExistenceAssert);
}
private List<VirtualFile> distribute(ExecutorService executorService)
@@ -454,12 +459,14 @@
for (DexProgramClass clazz : appView.appInfo().classes()) {
EnclosingMethodAttribute enclosingMethod = clazz.getEnclosingMethodAttribute();
List<InnerClassAttribute> innerClasses = clazz.getInnerClasses();
- if (enclosingMethod == null && innerClasses.isEmpty()) {
+ if (enclosingMethod == null
+ && innerClasses.isEmpty()
+ && clazz.getClassSignature().hasNoSignature()) {
continue;
}
// EnclosingMember translates directly to an enclosing class/method if present.
- List<DexAnnotation> annotations = new ArrayList<>(1 + innerClasses.size());
+ List<DexAnnotation> annotations = new ArrayList<>(2 + innerClasses.size());
if (enclosingMethod != null) {
if (enclosingMethod.getEnclosingMethod() != null) {
annotations.add(
@@ -507,6 +514,13 @@
}
}
+ 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 =
@@ -520,6 +534,7 @@
// Clear the attribute structures now that they are represented in annotations.
clazz.clearEnclosingMethodAttribute();
clazz.clearInnerClasses();
+ clazz.clearClassSignature();
}
}
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 978a7e7..711eb47 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -51,6 +51,8 @@
import com.android.tools.r8.graph.DexValue.DexValueNull;
import com.android.tools.r8.graph.EnclosingMethodAttribute;
import com.android.tools.r8.graph.FieldAccessFlags;
+import com.android.tools.r8.graph.GenericSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.OffsetToObjectMapping;
@@ -60,6 +62,7 @@
import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.Reporter;
import com.google.common.io.ByteStreams;
import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
@@ -757,7 +760,8 @@
}
AttributesAndAnnotations attrs =
- new AttributesAndAnnotations(type, annotationsDirectory.clazz, options.itemFactory);
+ new AttributesAndAnnotations(
+ type, origin, annotationsDirectory.clazz, options.itemFactory, options.reporter);
Long finalChecksum = checksum;
ChecksumSupplier checksumSupplier =
@@ -776,6 +780,7 @@
Collections.emptyList(),
attrs.getEnclosingMethodAttribute(),
attrs.getInnerClasses(),
+ attrs.classSignature,
attrs.getAnnotations(),
staticFields,
instanceFields,
@@ -1327,6 +1332,7 @@
private EnclosingMethodAttribute enclosingMethodAttribute = null;
private List<InnerClassAttribute> innerClasses = null;
private List<DexAnnotation> lazyAnnotations = null;
+ private ClassSignature classSignature = ClassSignature.NO_CLASS_SIGNATURE;
public DexAnnotationSet getAnnotations() {
if (lazyAnnotations != null) {
@@ -1346,8 +1352,16 @@
return enclosingMethodAttribute;
}
+ public ClassSignature getClassSignature() {
+ return classSignature;
+ }
+
public AttributesAndAnnotations(
- DexType type, DexAnnotationSet annotations, DexItemFactory factory) {
+ DexType type,
+ Origin origin,
+ DexAnnotationSet annotations,
+ DexItemFactory factory,
+ Reporter reporter) {
this.originalAnnotations = annotations;
DexType enclosingClass = null;
DexMethod enclosingMethod = null;
@@ -1378,6 +1392,12 @@
} else {
memberClasses.addAll(members);
}
+ } else if (DexAnnotation.isSignatureAnnotation(annotation, factory)) {
+ ensureAnnotations(i);
+ String signature = DexAnnotation.getSignature(annotation);
+ classSignature =
+ GenericSignature.parseClassSignature(
+ type.getName(), signature, origin, factory, reporter);
} else {
copyAnnotation(annotation);
}
diff --git a/src/main/java/com/android/tools/r8/graph/AccessFlags.java b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
index 4bbcead..c169b25 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
@@ -85,11 +85,11 @@
public boolean isMoreVisibleThan(
AccessFlags other, String packageNameThis, String packageNameOther) {
- int visibilityOrdinal = visibilityOrdinal();
- if (visibilityOrdinal > other.visibilityOrdinal()) {
+ int visibilityOrdinal = getVisibilityOrdinal();
+ if (visibilityOrdinal > other.getVisibilityOrdinal()) {
return true;
}
- if (visibilityOrdinal == other.visibilityOrdinal()
+ if (visibilityOrdinal == other.getVisibilityOrdinal()
&& isVisibilityDependingOnPackage()
&& !packageNameThis.equals(packageNameOther)) {
return true;
@@ -98,14 +98,14 @@
}
public boolean isAtLeastAsVisibleAs(AccessFlags other) {
- return visibilityOrdinal() >= other.visibilityOrdinal();
+ return getVisibilityOrdinal() >= other.getVisibilityOrdinal();
}
public boolean isSameVisibility(AccessFlags other) {
- return visibilityOrdinal() == other.visibilityOrdinal();
+ return getVisibilityOrdinal() == other.getVisibilityOrdinal();
}
- private int visibilityOrdinal() {
+ public int getVisibilityOrdinal() {
// public > protected > package > private
if (isPublic()) {
return 3;
@@ -121,7 +121,7 @@
}
public boolean isVisibilityDependingOnPackage() {
- return visibilityOrdinal() == 1 || visibilityOrdinal() == 2;
+ return getVisibilityOrdinal() == 1 || getVisibilityOrdinal() == 2;
}
public boolean isPackagePrivate() {
diff --git a/src/main/java/com/android/tools/r8/graph/AppServices.java b/src/main/java/com/android/tools/r8/graph/AppServices.java
index ffe915a..e89f65b 100644
--- a/src/main/java/com/android/tools/r8/graph/AppServices.java
+++ b/src/main/java/com/android/tools/r8/graph/AppServices.java
@@ -331,11 +331,8 @@
}
private void warn(String message, DexType type, Origin origin) {
- if (true) {
- // TODO(b/169753370): Re-enable again after roll.
- return;
- }
- if (!options.getProguardConfiguration().getDontWarnPatterns().matches(type)) {
+ if (options.getProguardConfiguration() == null
+ || !options.getProguardConfiguration().getDontWarnPatterns().matches(type)) {
options.reporter.warning(new StringDiagnostic(message, origin));
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/ClassKind.java b/src/main/java/com/android/tools/r8/graph/ClassKind.java
index 14fa13e..3929cfb 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassKind.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassKind.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.origin.Origin;
import java.util.List;
import java.util.function.Consumer;
@@ -26,6 +27,7 @@
nestMembers,
enclosingMember,
innerClasses,
+ classSignature,
annotations,
staticFields,
instanceFields,
@@ -45,6 +47,7 @@
nestMembers,
enclosingMember,
innerClasses,
+ classSignature,
annotations,
staticFields,
instanceFields,
@@ -65,6 +68,7 @@
nestMembers,
enclosingMember,
innerClasses,
+ classSignature,
annotations,
staticFields,
instanceFields,
@@ -84,6 +88,7 @@
nestMembers,
enclosingMember,
innerClasses,
+ classSignature,
annotations,
staticFields,
instanceFields,
@@ -106,6 +111,7 @@
List<NestMemberClassAttribute> nestMembers,
EnclosingMethodAttribute enclosingMember,
List<InnerClassAttribute> innerClasses,
+ ClassSignature classSignature,
DexAnnotationSet annotations,
DexEncodedField[] staticFields,
DexEncodedField[] instanceFields,
@@ -135,6 +141,7 @@
List<NestMemberClassAttribute> nestMembers,
EnclosingMethodAttribute enclosingMember,
List<InnerClassAttribute> innerClasses,
+ ClassSignature classSignature,
DexAnnotationSet annotations,
DexEncodedField[] staticFields,
DexEncodedField[] instanceFields,
@@ -154,6 +161,7 @@
nestMembers,
enclosingMember,
innerClasses,
+ classSignature,
annotations,
staticFields,
instanceFields,
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index dede4e0..d883e3e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -6,10 +6,12 @@
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OptionalBool;
+import com.android.tools.r8.utils.TraversalContinuation;
import com.google.common.base.MoreObjects;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
@@ -24,6 +26,7 @@
import java.util.ListIterator;
import java.util.Set;
import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.function.Predicate;
public abstract class DexClass extends DexDefinition {
@@ -59,6 +62,9 @@
private NestHostClassAttribute nestHost;
private final List<NestMemberClassAttribute> nestMembers;
+ /** Generic signature information if the attribute is present in the input */
+ protected ClassSignature classSignature;
+
public DexClass(
DexString sourceFile,
DexTypeList interfaces,
@@ -73,6 +79,7 @@
List<NestMemberClassAttribute> nestMembers,
EnclosingMethodAttribute enclosingMethod,
List<InnerClassAttribute> innerClasses,
+ ClassSignature classSignature,
DexAnnotationSet annotations,
Origin origin,
boolean skipNameValidationForTesting) {
@@ -92,6 +99,9 @@
assert nestMembers != null;
this.enclosingMethod = enclosingMethod;
this.innerClasses = innerClasses;
+ assert classSignature != null;
+ this.classSignature = classSignature;
+ assert GenericSignatureUtils.verifyNoDuplicateGenericDefinitions(classSignature, annotations);
if (type == superType) {
throw new CompilationError("Class " + type.toString() + " cannot extend itself");
}
@@ -231,6 +241,15 @@
}
}
+ public TraversalContinuation traverseFields(Function<DexEncodedField, TraversalContinuation> fn) {
+ for (DexEncodedField field : fields()) {
+ if (fn.apply(field).shouldBreak()) {
+ return TraversalContinuation.BREAK;
+ }
+ }
+ return TraversalContinuation.CONTINUE;
+ }
+
public List<DexEncodedField> staticFields() {
assert staticFields != null;
if (InternalOptions.assertionsEnabled()) {
@@ -749,6 +768,10 @@
innerClasses.clear();
}
+ public void clearClassSignature() {
+ classSignature = ClassSignature.NO_CLASS_SIGNATURE;
+ }
+
public void removeInnerClasses(Predicate<InnerClassAttribute> predicate) {
innerClasses.removeIf(predicate::test);
}
@@ -774,6 +797,14 @@
throw new Unreachable();
}
+ public ClassSignature getClassSignature() {
+ return classSignature;
+ }
+
+ public void setClassSignature(ClassSignature classSignature) {
+ this.classSignature = classSignature;
+ }
+
public boolean isLocalClass() {
InnerClassAttribute innerClass = getInnerClassAttributeForThisClass();
// The corresponding enclosing-method attribute might be not available, e.g., CF version 50.
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndField.java b/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
index 288b336..c0a3125 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
@@ -19,6 +19,11 @@
}
}
+ @Override
+ public FieldAccessFlags getAccessFlags() {
+ return getDefinition().getAccessFlags();
+ }
+
public boolean isProgramField() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
index 0bc5348..9dd5795 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
@@ -21,6 +21,8 @@
this.definition = definition;
}
+ public abstract AccessFlags<?> getAccessFlags();
+
public DexType getContextType() {
return getHolderType();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
index 87d1bb9..115bf84 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
@@ -24,6 +24,11 @@
}
@Override
+ public MethodAccessFlags getAccessFlags() {
+ return getDefinition().getAccessFlags();
+ }
+
+ @Override
public boolean isMethodTarget() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
index 118a04d..410f985 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
import com.android.tools.r8.origin.Origin;
import java.util.List;
@@ -26,6 +27,7 @@
List<NestMemberClassAttribute> nestMembers,
EnclosingMethodAttribute enclosingMember,
List<InnerClassAttribute> innerClasses,
+ ClassSignature classSignature,
DexAnnotationSet annotations,
DexEncodedField[] staticFields,
DexEncodedField[] instanceFields,
@@ -46,6 +48,7 @@
nestMembers,
enclosingMember,
innerClasses,
+ classSignature,
annotations,
origin,
skipNameValidationForTesting);
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 fcb79fe..43c065a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexField.java
@@ -6,6 +6,8 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.Reference;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -37,6 +39,11 @@
}
@Override
+ public ProgramField lookupOnProgramClass(DexProgramClass clazz) {
+ return clazz != null ? clazz.lookupProgramField(this) : null;
+ }
+
+ @Override
public <T> T apply(
Function<DexType, T> classConsumer,
Function<DexField, T> fieldConsumer,
@@ -161,4 +168,11 @@
public DexField withHolder(DexType holder, DexItemFactory dexItemFactory) {
return dexItemFactory.createField(holder, type, name);
}
+
+ public FieldReference asFieldReference() {
+ return Reference.field(
+ Reference.classFromDescriptor(holder.toDescriptorString()),
+ name.toString(),
+ Reference.typeFromDescriptor(type.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 cfccc2b..cdf97ab 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -62,6 +62,7 @@
public class DexItemFactory {
public static final String throwableDescriptorString = "Ljava/lang/Throwable;";
+ public static final String dalvikAnnotationSignatureString = "Ldalvik/annotation/Signature;";
/** Set of types that may be synthesized during compilation. */
private final Set<DexType> possibleCompilerSynthesizedTypes = Sets.newIdentityHashSet();
@@ -541,7 +542,7 @@
public final DexType annotationMethodParameters =
createStaticallyKnownType("Ldalvik/annotation/MethodParameters;");
public final DexType annotationSignature =
- createStaticallyKnownType("Ldalvik/annotation/Signature;");
+ createStaticallyKnownType(dalvikAnnotationSignatureString);
public final DexType annotationSourceDebugExtension =
createStaticallyKnownType("Ldalvik/annotation/SourceDebugExtension;");
public final DexType annotationThrows = createStaticallyKnownType("Ldalvik/annotation/Throws;");
@@ -2261,6 +2262,7 @@
});
}
+ @Deprecated
synchronized public void forAllTypes(Consumer<DexType> f) {
new ArrayList<>(types.values()).forEach(f);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
index 22920eb..dcb4ed3 100644
--- a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
import com.android.tools.r8.origin.Origin;
import java.util.Arrays;
@@ -27,6 +28,7 @@
List<NestMemberClassAttribute> nestMembers,
EnclosingMethodAttribute enclosingMember,
List<InnerClassAttribute> innerClasses,
+ ClassSignature classSignature,
DexAnnotationSet annotations,
DexEncodedField[] staticFields,
DexEncodedField[] instanceFields,
@@ -47,6 +49,7 @@
nestMembers,
enclosingMember,
innerClasses,
+ classSignature,
annotations,
origin,
skipNameValidationForTesting);
diff --git a/src/main/java/com/android/tools/r8/graph/DexMember.java b/src/main/java/com/android/tools/r8/graph/DexMember.java
index 2a429d6..a1767c3 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMember.java
@@ -16,9 +16,9 @@
this.name = name;
}
- public DexEncodedMember<?, ?> lookupOnClass(DexClass clazz) {
- return clazz != null ? clazz.lookupMember(this) : null;
- }
+ public abstract DexEncodedMember<?, ?> lookupOnClass(DexClass clazz);
+
+ public abstract ProgramMember<?, ?> lookupOnProgramClass(DexProgramClass clazz);
public abstract boolean match(R entry);
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index 128b353..212f5ca 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -75,6 +75,7 @@
return clazz != null ? clazz.lookupMember(this) : null;
}
+ @Override
public ProgramMethod lookupOnProgramClass(DexProgramClass clazz) {
return clazz != null ? clazz.lookupProgramMethod(this) : null;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index b5fe6b8..e61ff61 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import static com.android.tools.r8.graph.GenericSignature.EMPTY_TYPE_ARGUMENTS;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
import static com.google.common.base.Predicates.alwaysTrue;
@@ -11,12 +12,15 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.TraversalContinuation;
+import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -62,6 +66,7 @@
List<NestMemberClassAttribute> nestMembers,
EnclosingMethodAttribute enclosingMember,
List<InnerClassAttribute> innerClasses,
+ ClassSignature classSignature,
DexAnnotationSet classAnnotations,
DexEncodedField[] staticFields,
DexEncodedField[] instanceFields,
@@ -81,6 +86,7 @@
nestMembers,
enclosingMember,
innerClasses,
+ classSignature,
classAnnotations,
staticFields,
instanceFields,
@@ -103,6 +109,7 @@
List<NestMemberClassAttribute> nestMembers,
EnclosingMethodAttribute enclosingMember,
List<InnerClassAttribute> innerClasses,
+ ClassSignature classSignature,
DexAnnotationSet classAnnotations,
DexEncodedField[] staticFields,
DexEncodedField[] instanceFields,
@@ -125,6 +132,7 @@
nestMembers,
enclosingMember,
innerClasses,
+ classSignature,
classAnnotations,
origin,
skipNameValidationForTesting);
@@ -182,6 +190,17 @@
return toProgramMethodOrNull(getInitializer(types));
}
+ /** Find member in this class matching {@param member}. */
+ @SuppressWarnings("unchecked")
+ public <D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
+ ProgramMember<D, R> lookupProgramMember(DexMember<D, R> member) {
+ ProgramMember<?, ?> definition =
+ member.isDexField()
+ ? lookupProgramField(member.asDexField())
+ : lookupProgramMethod(member.asDexMethod());
+ return (ProgramMember<D, R>) definition;
+ }
+
public ProgramField lookupProgramField(DexField reference) {
return toProgramFieldOrNull(lookupField(reference));
}
@@ -204,8 +223,22 @@
return null;
}
+ public TraversalContinuation traverseProgramMembers(
+ Function<ProgramMember<?, ?>, TraversalContinuation> fn) {
+ TraversalContinuation continuation = traverseProgramFields(fn);
+ if (continuation.shouldContinue()) {
+ return traverseProgramMethods(fn);
+ }
+ return TraversalContinuation.BREAK;
+ }
+
+ public TraversalContinuation traverseProgramFields(
+ Function<? super ProgramField, TraversalContinuation> fn) {
+ return traverseFields(field -> fn.apply(new ProgramField(this, field)));
+ }
+
public TraversalContinuation traverseProgramMethods(
- Function<ProgramMethod, TraversalContinuation> fn) {
+ Function<? super ProgramMethod, TraversalContinuation> fn) {
return getMethodCollection().traverse(method -> fn.apply(new ProgramMethod(this, method)));
}
@@ -258,6 +291,7 @@
for (InnerClassAttribute attribute : getInnerClasses()) {
attribute.collectIndexedItems(indexedItems);
}
+ // We are explicitly not adding items referenced in signatures.
forEachProgramField(field -> field.collectIndexedItems(indexedItems));
forEachProgramMethod(method -> method.collectIndexedItems(indexedItems, graphLens, rewriter));
}
@@ -271,6 +305,7 @@
void collectMixedSectionItems(MixedSectionCollection mixedItems) {
assert getEnclosingMethodAttribute() == null;
assert getInnerClasses().isEmpty();
+ assert !classSignature.hasSignature();
if (hasClassOrMemberAnnotations()) {
mixedItems.setAnnotationsDirectoryForClass(this, new DexAnnotationDirectory(this));
}
@@ -280,6 +315,7 @@
public void addDependencies(MixedSectionCollection collector) {
assert getEnclosingMethodAttribute() == null;
assert getInnerClasses().isEmpty();
+ assert !classSignature.hasSignature();
// We only have a class data item if there are methods or fields.
if (hasMethodsOrFields()) {
collector.add(this);
@@ -449,7 +485,7 @@
return;
}
addExtraInterfacesToInterfacesArray(extraInterfaces);
- addExtraInterfacesToSignatureAnnotationIfPresent(extraInterfaces, factory);
+ addExtraInterfacesToSignatureIfPresent(extraInterfaces);
}
private void addExtraInterfacesToInterfacesArray(List<DexType> extraInterfaces) {
@@ -461,31 +497,22 @@
interfaces = new DexTypeList(newInterfaces);
}
- private void addExtraInterfacesToSignatureAnnotationIfPresent(
- List<DexType> extraInterfaces, DexItemFactory factory) {
- // We need to introduce in the dalvik.annotation.Signature annotation the extra interfaces.
+ private void addExtraInterfacesToSignatureIfPresent(List<DexType> extraInterfaces) {
+ // We need to introduce the extra interfaces to the generic signature.
// At this point we cheat and pretend the extraInterfaces simply don't use any generic types.
- DexAnnotation[] annotations = annotations().annotations;
- for (int i = 0; i < annotations.length; i++) {
- DexAnnotation annotation = annotations[i];
- if (DexAnnotation.isSignatureAnnotation(annotation, factory)) {
- DexAnnotation[] rewrittenAnnotations = annotations.clone();
- rewrittenAnnotations[i] = rewriteSignatureAnnotation(annotation, extraInterfaces, factory);
- setAnnotations(new DexAnnotationSet(rewrittenAnnotations));
- // There is at most one signature annotation, so we can return here.
- return;
- }
+ if (classSignature.hasNoSignature() || extraInterfaces.isEmpty()) {
+ return;
}
- }
-
- private DexAnnotation rewriteSignatureAnnotation(
- DexAnnotation annotation, List<DexType> extraInterfaces, DexItemFactory factory) {
- String signature = DexAnnotation.getSignature(annotation);
- StringBuilder newSignatureBuilder = new StringBuilder(signature);
+ ImmutableList.Builder<ClassTypeSignature> interfacesBuilder =
+ ImmutableList.<ClassTypeSignature>builder().addAll(classSignature.superInterfaceSignatures);
for (DexType extraInterface : extraInterfaces) {
- newSignatureBuilder.append(extraInterface.descriptor.toString());
+ interfacesBuilder.add(new ClassTypeSignature(extraInterface, EMPTY_TYPE_ARGUMENTS));
}
- return DexAnnotation.createSignatureAnnotation(newSignatureBuilder.toString(), factory);
+ classSignature =
+ new ClassSignature(
+ classSignature.formalTypeParameters,
+ classSignature.superClassSignature,
+ interfacesBuilder.build());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexString.java b/src/main/java/com/android/tools/r8/graph/DexString.java
index 602a6b7..b5d50f0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexString.java
+++ b/src/main/java/com/android/tools/r8/graph/DexString.java
@@ -500,4 +500,11 @@
}
return arrayDim;
}
+
+ public DexString toArrayDescriptor(int dimensions, DexItemFactory dexItemFactory) {
+ byte[] newContent = new byte[content.length + dimensions];
+ Arrays.fill(newContent, 0, dimensions, (byte) '[');
+ System.arraycopy(content, 0, newContent, dimensions, content.length);
+ return dexItemFactory.createString(size + dimensions, newContent);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index f21e965..b1892bf 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -52,6 +52,10 @@
this.descriptor = descriptor;
}
+ public DexString getDescriptor() {
+ return descriptor;
+ }
+
@Override
public int computeHashCode() {
return descriptor.hashCode();
@@ -431,11 +435,7 @@
}
public DexType toArrayType(int dimensions, DexItemFactory dexItemFactory) {
- byte[] content = new byte[descriptor.content.length + dimensions];
- Arrays.fill(content, 0, dimensions, (byte) '[');
- System.arraycopy(descriptor.content, 0, content, dimensions, descriptor.content.length);
- DexString newDesc = dexItemFactory.createString(descriptor.size + dimensions, content);
- return dexItemFactory.createType(newDesc);
+ return dexItemFactory.createType(descriptor.toArrayDescriptor(dimensions, dexItemFactory));
}
public DexType toArrayElementType(DexItemFactory dexItemFactory) {
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 2427913..5cd3f8f 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignature.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
@@ -5,8 +5,10 @@
import static com.android.tools.r8.utils.DescriptorUtils.getClassBinaryNameFromDescriptor;
import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromClassBinaryName;
+import static com.google.common.base.Predicates.alwaysTrue;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.Reporter;
@@ -14,6 +16,7 @@
import java.lang.reflect.GenericSignatureFormatError;
import java.nio.CharBuffer;
import java.util.List;
+import java.util.function.Predicate;
/**
* Internal encoding of the generics signature attribute as defined by JVMS 7 $ 4.3.4.
@@ -217,6 +220,21 @@
visitor.visitSuperInterface(superInterface);
}
}
+
+ public String toRenamedString(NamingLens namingLens, Predicate<DexType> isTypeMissing) {
+ if (hasNoSignature()) {
+ return null;
+ }
+ GenericSignaturePrinter genericSignaturePrinter =
+ new GenericSignaturePrinter(namingLens, isTypeMissing);
+ genericSignaturePrinter.visitClassSignature(this);
+ return genericSignaturePrinter.toString();
+ }
+
+ @Override
+ public String toString() {
+ return toRenamedString(NamingLens.getIdentityLens(), alwaysTrue());
+ }
}
public abstract static class TypeSignature {
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java b/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java
new file mode 100644
index 0000000..1b23c85
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java
@@ -0,0 +1,148 @@
+// 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;
+
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
+import com.android.tools.r8.graph.GenericSignature.TypeSignature;
+import com.android.tools.r8.graph.GenericSignature.WildcardIndicator;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.util.List;
+import java.util.function.Predicate;
+
+public class GenericSignaturePrinter implements GenericSignatureVisitor {
+
+ private final NamingLens namingLens;
+ private final Predicate<DexType> isTypeMissing;
+
+ public GenericSignaturePrinter(NamingLens namingLens, Predicate<DexType> isTypeMissing) {
+ this.namingLens = namingLens;
+ this.isTypeMissing = isTypeMissing;
+ }
+
+ private final StringBuilder sb = new StringBuilder();
+
+ @Override
+ public void visitClassSignature(ClassSignature classSignature) {
+ classSignature.visit(this);
+ }
+
+ @Override
+ public void visitFormalTypeParameters(List<FormalTypeParameter> formalTypeParameters) {
+ if (formalTypeParameters.isEmpty()) {
+ return;
+ }
+ sb.append("<");
+ for (FormalTypeParameter formalTypeParameter : formalTypeParameters) {
+ sb.append(formalTypeParameter.name);
+ formalTypeParameter.visit(this);
+ }
+ sb.append(">");
+ }
+
+ @Override
+ public void visitClassBound(FieldTypeSignature fieldSignature) {
+ sb.append(":");
+ printFieldTypeSignature(fieldSignature, false);
+ }
+
+ @Override
+ public void visitInterfaceBound(FieldTypeSignature fieldSignature) {
+ sb.append(":");
+ printFieldTypeSignature(fieldSignature, false);
+ }
+
+ @Override
+ public void visitSuperClass(ClassTypeSignature classTypeSignature) {
+ printFieldTypeSignature(classTypeSignature, false);
+ }
+
+ @Override
+ public void visitSuperInterface(ClassTypeSignature classTypeSignature) {
+ printFieldTypeSignature(classTypeSignature, false);
+ }
+
+ @Override
+ public void visitTypeSignature(TypeSignature typeSignature) {
+ if (typeSignature.isBaseTypeSignature()) {
+ DexType type = typeSignature.asBaseTypeSignature().type;
+ sb.append(type.toDescriptorString());
+ } else {
+ printFieldTypeSignature(typeSignature.asFieldTypeSignature(), false);
+ }
+ }
+
+ @Override
+ public void visitSimpleClass(ClassTypeSignature classTypeSignature) {
+ printFieldTypeSignature(classTypeSignature, true);
+ }
+
+ @Override
+ public void visitTypeArguments(List<FieldTypeSignature> typeArguments) {
+ if (typeArguments.isEmpty()) {
+ return;
+ }
+ sb.append("<");
+ for (FieldTypeSignature typeArgument : typeArguments) {
+ WildcardIndicator wildcardIndicator = typeArgument.getWildcardIndicator();
+ if (wildcardIndicator != WildcardIndicator.NONE) {
+ assert wildcardIndicator != WildcardIndicator.NOT_AN_ARGUMENT;
+ sb.append(wildcardIndicator == WildcardIndicator.POSITIVE ? "+" : "-");
+ }
+ visitTypeSignature(typeArgument);
+ }
+ sb.append(">");
+ }
+
+ private void printFieldTypeSignature(
+ FieldTypeSignature fieldTypeSignature, boolean printingInner) {
+ // For inner member classes we only print the inner name and the type-arguments.
+ if (fieldTypeSignature.isStar()) {
+ sb.append("*");
+ } else if (fieldTypeSignature.isTypeVariableSignature()) {
+ sb.append("T").append(fieldTypeSignature.asTypeVariableSignature().typeVariable).append(";");
+ } else if (fieldTypeSignature.isArrayTypeSignature()) {
+ sb.append("[");
+ fieldTypeSignature.asArrayTypeSignature().visit(this);
+ } else {
+ assert fieldTypeSignature.isClassTypeSignature();
+ ClassTypeSignature classTypeSignature = fieldTypeSignature.asClassTypeSignature();
+ if (classTypeSignature.isNoSignature()) {
+ return;
+ }
+ String renamedString = namingLens.lookupDescriptor(classTypeSignature.type).toString();
+ if (!printingInner) {
+ sb.append("L").append(DescriptorUtils.getBinaryNameFromDescriptor(renamedString));
+ } else {
+ assert classTypeSignature.enclosingTypeSignature != null;
+ DexType enclosingType = classTypeSignature.enclosingTypeSignature.type;
+ String outerDescriptor = namingLens.lookupDescriptor(enclosingType).toString();
+ String innerClassName = DescriptorUtils.getInnerClassName(outerDescriptor, renamedString);
+ if (innerClassName == null && isTypeMissing.test(classTypeSignature.type)) {
+ assert renamedString.equals(classTypeSignature.type.toDescriptorString());
+ innerClassName =
+ DescriptorUtils.getInnerClassName(enclosingType.toDescriptorString(), renamedString);
+ }
+ if (innerClassName == null) {
+ // We can no longer encode the inner name in the generic signature.
+ return;
+ }
+ sb.append(".").append(innerClassName);
+ }
+ classTypeSignature.visit(this);
+ if (!printingInner) {
+ sb.append(";");
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return sb.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
new file mode 100644
index 0000000..ddabc37
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
@@ -0,0 +1,241 @@
+// 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;
+
+import static com.android.tools.r8.graph.GenericSignature.EMPTY_SUPER_INTERFACES;
+import static com.android.tools.r8.graph.GenericSignature.EMPTY_TYPE_ARGUMENTS;
+import static com.android.tools.r8.graph.GenericSignature.EMPTY_TYPE_PARAMS;
+import static com.android.tools.r8.graph.GenericSignature.NO_FIELD_TYPE_SIGNATURE;
+import static com.android.tools.r8.graph.GenericSignature.StarFieldTypeSignature.STAR_FIELD_TYPE_SIGNATURE;
+
+import com.android.tools.r8.graph.GenericSignature.ArrayTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
+import com.android.tools.r8.graph.GenericSignature.TypeSignature;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.ArrayList;
+import java.util.List;
+
+public class GenericSignatureTypeRewriter {
+
+ private final AppView<?> appView;
+ private final DexProgramClass context;
+
+ private final FieldTypeSignature objectTypeSignature;
+
+ public GenericSignatureTypeRewriter(AppView<?> appView, DexProgramClass context) {
+ this.appView = appView;
+ this.context = context;
+ objectTypeSignature =
+ new ClassTypeSignature(appView.dexItemFactory().objectType, EMPTY_TYPE_ARGUMENTS);
+ }
+
+ public ClassSignature rewrite(ClassSignature classSignature) {
+ if (classSignature.hasNoSignature() || appView.graphLens().isIdentityLens()) {
+ return classSignature;
+ }
+ return new ClassSignatureRewriter().run(classSignature);
+ }
+
+ private class ClassSignatureRewriter implements GenericSignatureVisitor {
+
+ private final List<FormalTypeParameter> rewrittenTypeParameters = new ArrayList<>();
+ private ClassTypeSignature rewrittenSuperClass;
+ private final List<ClassTypeSignature> rewrittenSuperInterfaces = new ArrayList<>();
+
+ @Override
+ public void visitClassSignature(ClassSignature classSignature) {
+ classSignature.visit(this);
+ }
+
+ @Override
+ public void visitFormalTypeParameters(List<FormalTypeParameter> formalTypeParameters) {
+ for (FormalTypeParameter formalTypeParameter : formalTypeParameters) {
+ rewrittenTypeParameters.add(new FormalTypeParameterRewriter().run(formalTypeParameter));
+ }
+ }
+
+ @Override
+ public void visitSuperClass(ClassTypeSignature classTypeSignature) {
+ rewrittenSuperClass = new ClassTypeSignatureRewriter(true).run(classTypeSignature);
+ if (rewrittenSuperClass == null) {
+ rewrittenSuperClass =
+ new ClassTypeSignature(appView.dexItemFactory().objectType, EMPTY_TYPE_ARGUMENTS);
+ }
+ }
+
+ @Override
+ public void visitSuperInterface(ClassTypeSignature classTypeSignature) {
+ ClassTypeSignature superInterface =
+ new ClassTypeSignatureRewriter(true).run(classTypeSignature);
+ if (superInterface != null) {
+ rewrittenSuperInterfaces.add(superInterface);
+ }
+ }
+
+ private ClassSignature run(ClassSignature classSignature) {
+ classSignature.visit(this);
+ if (rewrittenTypeParameters.isEmpty()
+ && rewrittenSuperInterfaces.isEmpty()
+ && rewrittenSuperClass.isNoSignature()
+ && rewrittenSuperClass.type == appView.dexItemFactory().objectType) {
+ return ClassSignature.NO_CLASS_SIGNATURE;
+ }
+ return new ClassSignature(
+ rewrittenTypeParameters.isEmpty() ? EMPTY_TYPE_PARAMS : rewrittenTypeParameters,
+ rewrittenSuperClass,
+ rewrittenSuperInterfaces.isEmpty() ? EMPTY_SUPER_INTERFACES : rewrittenSuperInterfaces);
+ }
+ }
+
+ private class FormalTypeParameterRewriter implements GenericSignatureVisitor {
+
+ private FieldTypeSignature rewrittenClassBound = NO_FIELD_TYPE_SIGNATURE;
+ private final List<FieldTypeSignature> rewrittenInterfaceBounds = new ArrayList<>();
+
+ @Override
+ public void visitClassBound(FieldTypeSignature fieldSignature) {
+ rewrittenClassBound = new TypeSignatureRewriter().run(fieldSignature);
+ }
+
+ @Override
+ public void visitInterfaceBound(FieldTypeSignature fieldSignature) {
+ FieldTypeSignature interfaceBound = new TypeSignatureRewriter().run(fieldSignature);
+ if (interfaceBound != null) {
+ rewrittenInterfaceBounds.add(interfaceBound);
+ }
+ }
+
+ private FormalTypeParameter run(FormalTypeParameter formalTypeParameter) {
+ formalTypeParameter.visit(this);
+ // Guard against the case where we have <T::...> that is, no class or interfaces bounds.
+ if (rewrittenInterfaceBounds.isEmpty()
+ && (rewrittenClassBound == null || !rewrittenClassBound.hasSignature())) {
+ rewrittenClassBound = objectTypeSignature;
+ }
+ return new FormalTypeParameter(
+ formalTypeParameter.name,
+ rewrittenClassBound == null ? NO_FIELD_TYPE_SIGNATURE : rewrittenClassBound,
+ rewrittenInterfaceBounds.isEmpty() ? EMPTY_TYPE_ARGUMENTS : rewrittenInterfaceBounds);
+ }
+ }
+
+ private class TypeSignatureRewriter implements GenericSignatureVisitor {
+
+ private TypeSignature run(TypeSignature typeSignature) {
+ if (typeSignature.isBaseTypeSignature()) {
+ return typeSignature;
+ }
+ assert typeSignature.isFieldTypeSignature();
+ return run(typeSignature.asFieldTypeSignature());
+ }
+
+ private FieldTypeSignature run(FieldTypeSignature fieldTypeSignature) {
+ if (fieldTypeSignature.isStar()) {
+ return fieldTypeSignature;
+ }
+ if (fieldTypeSignature.isTypeVariableSignature()) {
+ return fieldTypeSignature;
+ }
+ if (fieldTypeSignature.isArrayTypeSignature()) {
+ ArrayTypeSignature arrayTypeSignature = fieldTypeSignature.asArrayTypeSignature();
+ TypeSignature rewrittenElement = run(arrayTypeSignature.elementSignature);
+ if (rewrittenElement == null) {
+ return new ArrayTypeSignature(objectTypeSignature);
+ }
+ return rewrittenElement.toArrayTypeSignature();
+ }
+ assert fieldTypeSignature.isClassTypeSignature();
+ ClassTypeSignature classTypeSignature = fieldTypeSignature.asClassTypeSignature();
+ if (classTypeSignature.isNoSignature()) {
+ return classTypeSignature;
+ }
+ return new ClassTypeSignatureRewriter(false).run(classTypeSignature);
+ }
+ }
+
+ private class ClassTypeSignatureRewriter implements GenericSignatureVisitor {
+
+ private final AppInfoWithLiveness appInfoWithLiveness;
+ private final boolean isSuperClassOrInterface;
+
+ // These fields are updated when iterating the modeled structure.
+ private DexType currentType;
+
+ // The following references are used to have a head and tail pointer to the classTypeSignature
+ // link we are building. The topClassSignature will have a reference to the top-most package
+ // and class-name. The parentClassSignature is a pointer pointing to the tail always and will
+ // be linked and updated when calling ClassTypeSignature.link.
+ private ClassTypeSignature topClassSignature;
+ private ClassTypeSignature parentClassSignature;
+
+ private ClassTypeSignatureRewriter(boolean isSuperClassOrInterface) {
+ appInfoWithLiveness =
+ appView.appInfo().hasLiveness() ? appView.appInfo().withLiveness() : null;
+ this.isSuperClassOrInterface = isSuperClassOrInterface;
+ }
+
+ @Override
+ public void visitSimpleClass(ClassTypeSignature classTypeSignature) {
+ currentType = getTarget(classTypeSignature.type);
+ if (currentType == null) {
+ return;
+ }
+ classTypeSignature.visit(this);
+ }
+
+ @Override
+ public void visitTypeArguments(List<FieldTypeSignature> typeArguments) {
+ ClassTypeSignature newClassTypeSignature;
+ if (typeArguments.isEmpty()) {
+ newClassTypeSignature = new ClassTypeSignature(currentType, EMPTY_TYPE_ARGUMENTS);
+ } else {
+ List<FieldTypeSignature> rewrittenTypeArguments = new ArrayList<>(typeArguments.size());
+ for (FieldTypeSignature typeArgument : typeArguments) {
+ if (typeArgument.isStar()) {
+ rewrittenTypeArguments.add(typeArgument);
+ continue;
+ }
+ FieldTypeSignature rewritten = new TypeSignatureRewriter().run(typeArgument);
+ if (rewritten != null) {
+ rewrittenTypeArguments.add(rewritten.asArgument(typeArgument.getWildcardIndicator()));
+ } else {
+ rewrittenTypeArguments.add(STAR_FIELD_TYPE_SIGNATURE);
+ }
+ }
+ newClassTypeSignature = new ClassTypeSignature(currentType, rewrittenTypeArguments);
+ }
+ if (topClassSignature == null) {
+ topClassSignature = newClassTypeSignature;
+ parentClassSignature = newClassTypeSignature;
+ } else {
+ ClassTypeSignature.link(parentClassSignature, newClassTypeSignature);
+ parentClassSignature = newClassTypeSignature;
+ }
+ }
+
+ private ClassTypeSignature run(ClassTypeSignature classTypeSignature) {
+ currentType = getTarget(classTypeSignature.type);
+ if (currentType == null) {
+ return null;
+ }
+ classTypeSignature.visit(this);
+ return topClassSignature;
+ }
+
+ private DexType getTarget(DexType type) {
+ if (appInfoWithLiveness != null && appInfoWithLiveness.wasPruned(type)) {
+ return null;
+ }
+ DexType rewrittenType = appView.graphLens().lookupType(type);
+ if (isSuperClassOrInterface && context.type == rewrittenType) {
+ return null;
+ }
+ return rewrittenType;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureUtils.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureUtils.java
new file mode 100644
index 0000000..7c0dcd2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureUtils.java
@@ -0,0 +1,34 @@
+// 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;
+
+import static com.android.tools.r8.graph.GenericSignature.ClassSignature.NO_CLASS_SIGNATURE;
+import static com.android.tools.r8.graph.GenericSignature.MethodTypeSignature.NO_METHOD_TYPE_SIGNATURE;
+import static com.android.tools.r8.graph.GenericSignature.NO_FIELD_TYPE_SIGNATURE;
+
+import com.android.tools.r8.graph.GenericSignature.DexDefinitionSignature;
+
+public class GenericSignatureUtils {
+
+ public static boolean verifyNoDuplicateGenericDefinitions(
+ DexDefinitionSignature<?> signature, DexAnnotationSet annotations) {
+ assert signature != null;
+ if (signature == NO_METHOD_TYPE_SIGNATURE
+ || signature == NO_FIELD_TYPE_SIGNATURE
+ || signature == NO_CLASS_SIGNATURE
+ || annotations == null) {
+ return true;
+ }
+ // The check is on the string descriptor to allow for not passing in a factory.
+ for (DexAnnotation annotation : annotations.annotations) {
+ assert !annotation
+ .getAnnotationType()
+ .descriptor
+ .toString()
+ .equals(DexItemFactory.dalvikAnnotationSignatureString);
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index b276437..7681f24 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -331,9 +331,7 @@
}
/** Lookup a rebound or non-rebound method reference using the current graph lens. */
- public MethodLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
- return internalLookupMethod(method, context, type, result -> result);
- }
+ public abstract MethodLookupResult lookupMethod(DexMethod method, DexMethod context, Type type);
protected abstract MethodLookupResult internalLookupMethod(
DexMethod reference, DexMethod context, Type type, LookupMethodContinuation continuation);
@@ -610,6 +608,10 @@
this.previousLens = previousLens;
}
+ public final DexItemFactory dexItemFactory() {
+ return dexItemFactory;
+ }
+
public final GraphLens getPrevious() {
return previousLens;
}
@@ -622,6 +624,21 @@
}
@Override
+ public MethodLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
+ if (method.getHolderType().isArrayType()) {
+ assert lookupType(method.getReturnType()) == method.getReturnType();
+ assert method.getParameters().stream()
+ .allMatch(parameterType -> lookupType(parameterType) == parameterType);
+ return MethodLookupResult.builder(this)
+ .setReference(method.withHolder(lookupType(method.getHolderType()), dexItemFactory))
+ .setType(type)
+ .build();
+ }
+ assert method.getHolderType().isClassType();
+ return internalLookupMethod(method, context, type, result -> result);
+ }
+
+ @Override
public final DexType lookupType(DexType type) {
if (type.isPrimitiveType() || type.isVoidType() || type.isNullValueType()) {
return type;
@@ -744,6 +761,11 @@
}
@Override
+ public MethodLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
+ return MethodLookupResult.builder(this).setReference(method).setType(type).build();
+ }
+
+ @Override
public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
DexMethod method) {
return RewrittenPrototypeDescription.none();
diff --git a/src/main/java/com/android/tools/r8/graph/InnerClassAttribute.java b/src/main/java/com/android/tools/r8/graph/InnerClassAttribute.java
index cd3baa0..f3195c9 100644
--- a/src/main/java/com/android/tools/r8/graph/InnerClassAttribute.java
+++ b/src/main/java/com/android/tools/r8/graph/InnerClassAttribute.java
@@ -91,17 +91,17 @@
}
}
- public DexType getLiveContext(AppInfoWithLiveness appInfo) {
+ public DexType getLiveContext(AppView<? extends AppInfoWithLiveness> appView) {
DexType context = getOuter();
if (context == null) {
- DexClass inner = appInfo.definitionFor(getInner());
+ DexClass inner = appView.definitionFor(getInner());
if (inner != null && inner.getEnclosingMethodAttribute() != null) {
EnclosingMethodAttribute enclosingMethodAttribute = inner.getEnclosingMethodAttribute();
if (enclosingMethodAttribute.getEnclosingClass() != null) {
context = enclosingMethodAttribute.getEnclosingClass();
} else {
DexMethod enclosingMethod = enclosingMethodAttribute.getEnclosingMethod();
- if (!appInfo.liveMethods.contains(enclosingMethod)) {
+ if (!appView.appInfo().liveMethods.contains(enclosingMethod)) {
// EnclosingMethodAttribute will be pruned as it references the pruned method.
// Hence, the current InnerClassAttribute will be removed too. No live context.
return null;
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 ea58f8d..4382eaa 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -32,6 +32,7 @@
import com.android.tools.r8.graph.DexValue.DexValueShort;
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.jar.CfApplicationWriter;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
@@ -206,6 +207,7 @@
private final List<NestMemberClassAttribute> nestMembers = new ArrayList<>();
private EnclosingMethodAttribute enclosingMember = null;
private final List<InnerClassAttribute> innerClasses = new ArrayList<>();
+ private ClassSignature classSignature = ClassSignature.NO_CLASS_SIGNATURE;
private List<DexAnnotation> annotations = null;
private List<DexAnnotationElement> defaultAnnotations = null;
private final List<DexEncodedField> staticFields = new ArrayList<>();
@@ -329,9 +331,9 @@
assert superName != null || name.equals(Constants.JAVA_LANG_OBJECT_NAME);
superType = superName == null ? null : application.getTypeFromName(superName);
this.interfaces = application.getTypeListFromNames(interfaces);
- if (signature != null && !signature.isEmpty()) {
- addAnnotation(DexAnnotation.createSignatureAnnotation(signature, application.getFactory()));
- }
+ classSignature =
+ GenericSignature.parseClassSignature(
+ name, signature, origin, application.getFactory(), application.options.reporter);
}
@Override
@@ -410,6 +412,7 @@
nestMembers,
enclosingMember,
innerClasses,
+ classSignature,
createAnnotationSet(annotations, application.options),
staticFields.toArray(DexEncodedField.EMPTY_ARRAY),
instanceFields.toArray(DexEncodedField.EMPTY_ARRAY),
@@ -567,8 +570,8 @@
this.desc = desc;
this.value = value;
if (signature != null && !signature.isEmpty()) {
- addAnnotation(DexAnnotation.createSignatureAnnotation(
- signature, parent.application.getFactory()));
+ addAnnotation(
+ DexAnnotation.createSignatureAnnotation(signature, parent.application.getFactory()));
}
}
@@ -648,7 +651,6 @@
private void addAnnotation(DexAnnotation annotation) {
getAnnotations().add(annotation);
}
-
private List<DexAnnotation> getAnnotations() {
if (annotations == null) {
annotations = new ArrayList<>();
diff --git a/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java b/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java
index 4b20148..8aa36c0 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodAccessInfoCollection.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.utils.MapUtils;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.collect.Sets;
+import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
@@ -41,11 +42,20 @@
return new ConcurrentBuilder();
}
+ public static IdentityBuilder identityBuilder() {
+ return new IdentityBuilder();
+ }
+
// TODO(b/132593519): We should not need sorted maps with the new member rebinding analysis.
public static SortedBuilder sortedBuilder() {
return new SortedBuilder();
}
+ public Modifier modifier() {
+ return new Modifier(
+ directInvokes, interfaceInvokes, staticInvokes, superInvokes, virtualInvokes);
+ }
+
public void forEachMethodReference(Consumer<DexMethod> method) {
Set<DexMethod> seen = Sets.newIdentityHashSet();
directInvokes.keySet().forEach(ConsumerUtils.acceptIfNotSeen(method, seen));
@@ -75,6 +85,10 @@
virtualInvokes.forEach(consumer);
}
+ public void forEachVirtualInvokeContext(DexMethod method, Consumer<ProgramMethod> consumer) {
+ virtualInvokes.getOrDefault(method, ProgramMethodSet.empty()).forEach(consumer);
+ }
+
public MethodAccessInfoCollection rewrittenWithLens(
DexDefinitionSupplier definitions, GraphLens lens) {
return new MethodAccessInfoCollection(
@@ -107,11 +121,16 @@
private final T virtualInvokes;
private Builder(Supplier<T> factory) {
- directInvokes = factory.get();
- interfaceInvokes = factory.get();
- staticInvokes = factory.get();
- superInvokes = factory.get();
- virtualInvokes = factory.get();
+ this(factory.get(), factory.get(), factory.get(), factory.get(), factory.get());
+ }
+
+ private Builder(
+ T directInvokes, T interfaceInvokes, T staticInvokes, T superInvokes, T virtualInvokes) {
+ this.directInvokes = directInvokes;
+ this.interfaceInvokes = interfaceInvokes;
+ this.staticInvokes = staticInvokes;
+ this.superInvokes = superInvokes;
+ this.virtualInvokes = virtualInvokes;
}
public T getDirectInvokes() {
@@ -138,23 +157,45 @@
return registerInvokeMethodInContext(invokedMethod, context, directInvokes);
}
+ public void registerInvokeDirectInContexts(DexMethod invokedMethod, ProgramMethodSet contexts) {
+ contexts.forEach(context -> registerInvokeDirectInContext(invokedMethod, context));
+ }
+
public boolean registerInvokeInterfaceInContext(
DexMethod invokedMethod, ProgramMethod context) {
return registerInvokeMethodInContext(invokedMethod, context, interfaceInvokes);
}
+ public void registerInvokeInterfaceInContexts(
+ DexMethod invokedMethod, ProgramMethodSet contexts) {
+ contexts.forEach(context -> registerInvokeInterfaceInContext(invokedMethod, context));
+ }
+
public boolean registerInvokeStaticInContext(DexMethod invokedMethod, ProgramMethod context) {
return registerInvokeMethodInContext(invokedMethod, context, staticInvokes);
}
+ public void registerInvokeStaticInContexts(DexMethod invokedMethod, ProgramMethodSet contexts) {
+ contexts.forEach(context -> registerInvokeStaticInContext(invokedMethod, context));
+ }
+
public boolean registerInvokeSuperInContext(DexMethod invokedMethod, ProgramMethod context) {
return registerInvokeMethodInContext(invokedMethod, context, superInvokes);
}
+ public void registerInvokeSuperInContexts(DexMethod invokedMethod, ProgramMethodSet contexts) {
+ contexts.forEach(context -> registerInvokeSuperInContext(invokedMethod, context));
+ }
+
public boolean registerInvokeVirtualInContext(DexMethod invokedMethod, ProgramMethod context) {
return registerInvokeMethodInContext(invokedMethod, context, virtualInvokes);
}
+ public void registerInvokeVirtualInContexts(
+ DexMethod invokedMethod, ProgramMethodSet contexts) {
+ contexts.forEach(context -> registerInvokeVirtualInContext(invokedMethod, context));
+ }
+
private static boolean registerInvokeMethodInContext(
DexMethod invokedMethod, ProgramMethod context, Map<DexMethod, ProgramMethodSet> invokes) {
return invokes
@@ -176,10 +217,38 @@
}
}
+ public static class IdentityBuilder
+ extends Builder<IdentityHashMap<DexMethod, ProgramMethodSet>> {
+
+ private IdentityBuilder() {
+ super(IdentityHashMap::new);
+ }
+ }
+
public static class SortedBuilder extends Builder<TreeMap<DexMethod, ProgramMethodSet>> {
private SortedBuilder() {
super(() -> new TreeMap<>(DexMethod::slowCompareTo));
}
}
+
+ public static class Modifier extends Builder<Map<DexMethod, ProgramMethodSet>> {
+
+ private Modifier(
+ Map<DexMethod, ProgramMethodSet> directInvokes,
+ Map<DexMethod, ProgramMethodSet> interfaceInvokes,
+ Map<DexMethod, ProgramMethodSet> staticInvokes,
+ Map<DexMethod, ProgramMethodSet> superInvokes,
+ Map<DexMethod, ProgramMethodSet> virtualInvokes) {
+ super(directInvokes, interfaceInvokes, staticInvokes, superInvokes, virtualInvokes);
+ }
+
+ public void addAll(MethodAccessInfoCollection collection) {
+ collection.forEachDirectInvoke(this::registerInvokeDirectInContexts);
+ collection.forEachInterfaceInvoke(this::registerInvokeInterfaceInContexts);
+ collection.forEachStaticInvoke(this::registerInvokeStaticInContexts);
+ collection.forEachSuperInvoke(this::registerInvokeSuperInContexts);
+ collection.forEachVirtualInvoke(this::registerInvokeVirtualInContexts);
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java b/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java
index a74f0c4..d877896 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodMapBacking.java
@@ -252,6 +252,9 @@
@Override
void replaceMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
+ // The code assumes that when replacement.apply(method) is called, the map is up-to-date with
+ // the previously replaced methods. We therefore cannot postpone the map updates to the end of
+ // the method.
ArrayList<DexEncodedMethod> initialValues = new ArrayList<>(methodMap.values());
for (DexEncodedMethod method : initialValues) {
DexEncodedMethod newMethod = replacement.apply(method);
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java b/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
index 883b0a6..82a133c 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramDefinition.java
@@ -8,7 +8,33 @@
DexProgramClass getContextClass();
+ AccessFlags<?> getAccessFlags();
+
DexType getContextType();
DexDefinition getDefinition();
+
+ default boolean isProgramClass() {
+ return false;
+ }
+
+ default DexProgramClass asProgramClass() {
+ return null;
+ }
+
+ default boolean isProgramField() {
+ return false;
+ }
+
+ default ProgramField asProgramField() {
+ return null;
+ }
+
+ default boolean isProgramMethod() {
+ return false;
+ }
+
+ default ProgramMethod asProgramMethod() {
+ return null;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
index a10b066..5f5a62b 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -113,6 +113,11 @@
methodMap = methodMap.remap(remapMethods, Function.identity(), Function.identity());
}
+ public Builder mapField(DexField from, DexField to) {
+ fieldMap.put(from, to);
+ return this;
+ }
+
/** Unidirectional mapping from one method to another. */
public Builder recordExtraOriginalSignature(DexMethod from, DexMethod to) {
methodMap.setRepresentative(from, to);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
index db92416..ec8d495 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Collections;
@@ -75,6 +76,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
+ ClassSignature.NO_CLASS_SIGNATURE,
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
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 b57adbf..e33137c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -389,8 +389,7 @@
DexField field = encodedField.field;
DexField newField = fixupFieldReference(field);
if (newField != encodedField.field) {
- // TODO(b/165498187): track mapped fields
- /* lensBuilder.map(field, newField); */
+ lensBuilder.mapField(field, newField);
setter.setField(i, encodedField.toTypeSubstitutedField(newField));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
index 5280f61..2d4fb70 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ResolutionResult;
@@ -111,6 +112,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
+ ClassSignature.NO_CLASS_SIGNATURE,
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
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 7936ffd..f2ea71d 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
@@ -25,6 +25,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.FieldAccessFlags;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.ir.conversion.IRConverter;
@@ -230,6 +231,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
+ ClassSignature.NO_CLASS_SIGNATURE,
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY, // No static fields.
new DexEncodedField[] {wrapperField},
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index e58eeba..6d37c32 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -26,6 +26,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
@@ -712,6 +713,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
+ ClassSignature.NO_CLASS_SIGNATURE,
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
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 20c3ae4..67d389f 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
@@ -26,6 +26,7 @@
import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
import com.android.tools.r8.graph.MethodAccessFlags;
@@ -213,6 +214,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
+ ClassSignature.NO_CLASS_SIGNATURE,
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
@@ -299,6 +301,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
+ ClassSignature.NO_CLASS_SIGNATURE,
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
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 ecad620..cc6fec7 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
@@ -23,6 +23,7 @@
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DexValue.DexValueNull;
import com.android.tools.r8.graph.FieldAccessFlags;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramMethod;
@@ -160,6 +161,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
+ ClassSignature.NO_CLASS_SIGNATURE,
DexAnnotationSet.empty(),
synthesizeStaticFields(),
synthesizeInstanceFields(),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
index 89dc96c..01b87d2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.NestMemberClassAttribute;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.UseRegistry;
@@ -185,6 +186,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
+ ClassSignature.NO_CLASS_SIGNATURE,
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
index 613fac6..7b0d6b0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.ir.code.IRCode;
@@ -151,6 +152,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
+ ClassSignature.NO_CLASS_SIGNATURE,
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 655ee2f..56d0a08 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -25,6 +25,7 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
@@ -1391,7 +1392,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
- // TODO: Build dex annotations structure.
+ ClassSignature.NO_CLASS_SIGNATURE,
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY, // Static fields.
DexEncodedField.EMPTY_ARRAY, // Instance fields.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
index b1fcc81..7a81edc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.MethodCollection;
import com.android.tools.r8.graph.ParameterAnnotationsList;
@@ -252,6 +253,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
+ ClassSignature.NO_CLASS_SIGNATURE,
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY, // Static fields.
DexEncodedField.EMPTY_ARRAY, // Instance fields.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index fbe8514..c766569 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -59,6 +59,7 @@
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback.OptimizationInfoFixer;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -358,7 +359,7 @@
ProgramMethodSet dependencies = enumUnboxingCandidatesInfo.allMethodDependencies();
enumUnboxingCandidatesInfo.clear();
// Update keep info on any of the enum methods of the removed classes.
- updateKeepInfo(enumClassesToUnbox);
+ updateKeepInfo(enumsToUnbox);
DirectMappedDexApplication.Builder appBuilder = appView.appInfo().app().asDirect().builder();
UnboxedEnumMemberRelocator relocator =
UnboxedEnumMemberRelocator.builder(appView)
@@ -414,26 +415,9 @@
});
}
- private void updateKeepInfo(Set<DexProgramClass> enumsToUnbox) {
- appView
- .appInfo()
- .getKeepInfo()
- .mutate(
- keepInfo -> {
- for (DexProgramClass enumToUnbox : enumsToUnbox) {
- assert !keepInfo.getClassInfo(enumToUnbox).isPinned();
- enumToUnbox.forEachProgramMethod(
- method -> {
- keepInfo.unsafeAllowMinificationOfMethod(method);
- keepInfo.unsafeUnpinMethod(method);
- });
- enumToUnbox.forEachProgramField(
- field -> {
- keepInfo.unsafeAllowMinificationOfField(field);
- keepInfo.unsafeUnpinField(field);
- });
- }
- });
+ private void updateKeepInfo(Set<DexType> enumsToUnbox) {
+ KeepInfoCollection keepInfo = appView.appInfo().getKeepInfo();
+ keepInfo.mutate(mutator -> mutator.removeKeepInfoForPrunedItems(enumsToUnbox));
}
public EnumInstanceFieldDataMap finishAnalysis() {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
index 9bc6826..d99bf3e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMember;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
@@ -14,9 +13,9 @@
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
@@ -44,40 +43,10 @@
originalMethodSignatures,
previousLens,
dexItemFactory);
- assert noDuplicateEntries(fieldMap, originalFieldSignatures);
- assert noDuplicateEntries(methodMap, originalMethodSignatures);
this.prototypeChangesPerMethod = prototypeChangesPerMethod;
this.unboxedEnums = unboxedEnums;
}
- private <T extends DexMember<?, ?>> boolean noDuplicateEntries(
- Map<T, T> map, BiMap<T, T> originalSignatures) {
- if (map.size() == originalSignatures.size()) {
- return true;
- }
- IdentityHashMap<T, T> methodMapReverse = new IdentityHashMap<>();
- IdentityHashMap<T, Set<T>> duplicate = new IdentityHashMap<>();
- map.forEach(
- (k, v) -> {
- if (methodMapReverse.containsKey(v)) {
- Set<T> dexMethods = duplicate.computeIfAbsent(v, ignored -> Sets.newIdentityHashSet());
- dexMethods.add(methodMapReverse.get(v));
- dexMethods.add(k);
- } else {
- methodMapReverse.put(v, k);
- }
- });
- assert !duplicate.isEmpty();
- StringBuilder sb = new StringBuilder();
- sb.append("Enum unboxing has created duplicate members: \n");
- duplicate.forEach(
- (target, origins) -> {
- sb.append(origins).append(" -> ").append(target).append("\n");
- });
- assert false : sb.toString();
- return false;
- }
-
@Override
protected RewrittenPrototypeDescription internalDescribePrototypeChanges(
RewrittenPrototypeDescription prototypeChanges, DexMethod method) {
@@ -99,26 +68,45 @@
return type;
}
- public static Builder builder() {
+ public static Builder enumUnboxingLensBuilder() {
return new Builder();
}
- static class Builder extends NestedGraphLens.Builder {
+ static class Builder {
+
+ protected final Map<DexType, DexType> typeMap = new IdentityHashMap<>();
+ protected final BiMap<DexField, DexField> originalFieldSignatures = HashBiMap.create();
+ protected final BiMap<DexMethod, DexMethod> originalMethodSignatures = HashBiMap.create();
private Map<DexMethod, RewrittenPrototypeDescription> prototypeChangesPerMethod =
new IdentityHashMap<>();
- public void move(DexMethod from, boolean fromStatic, DexMethod to, boolean toStatic) {
- move(from, fromStatic, to, toStatic, 0);
+ public void map(DexType from, DexType to) {
+ if (from == to) {
+ return;
+ }
+ typeMap.put(from, to);
+ }
+
+ public void move(DexField from, DexField to) {
+ if (from == to) {
+ return;
+ }
+ originalFieldSignatures.put(to, from);
+ }
+
+ public void move(DexMethod from, DexMethod to, boolean fromStatic, boolean toStatic) {
+ move(from, to, fromStatic, toStatic, 0);
}
public void move(
DexMethod from,
- boolean fromStatic,
DexMethod to,
+ boolean fromStatic,
boolean toStatic,
int numberOfExtraNullParameters) {
- super.move(from, to);
+ assert from != to;
+ originalMethodSignatures.put(to, from);
int offsetDiff = 0;
int toOffset = BooleanUtils.intValue(!toStatic);
RewrittenPrototypeDescription.ArgumentInfoCollection.Builder builder =
@@ -153,13 +141,15 @@
public EnumUnboxingLens build(
DexItemFactory dexItemFactory, GraphLens previousLens, Set<DexType> unboxedEnums) {
- if (typeMap.isEmpty() && methodMap.isEmpty() && fieldMap.isEmpty()) {
+ if (typeMap.isEmpty()
+ && originalFieldSignatures.isEmpty()
+ && originalMethodSignatures.isEmpty()) {
return null;
}
return new EnumUnboxingLens(
typeMap,
- methodMap,
- fieldMap,
+ originalMethodSignatures.inverse(),
+ originalFieldSignatures.inverse(),
originalFieldSignatures,
originalMethodSignatures,
previousLens,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index 634d74e..df641af 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -28,7 +28,7 @@
class EnumUnboxingTreeFixer {
private final Map<DexType, List<DexEncodedMethod>> unboxedEnumsMethods = new IdentityHashMap<>();
- private final EnumUnboxingLens.Builder lensBuilder = EnumUnboxingLens.builder();
+ private final EnumUnboxingLens.Builder lensBuilder = EnumUnboxingLens.enumUnboxingLensBuilder();
private final AppView<?> appView;
private final DexItemFactory factory;
private final Set<DexType> enumsToUnbox;
@@ -108,7 +108,7 @@
DexProto proto = encodedMethod.isStatic() ? method.proto : factory.prependHolderToProto(method);
DexMethod newMethod = factory.createMethod(newHolder, fixupProto(proto), newMethodName);
assert appView.definitionFor(encodedMethod.holder()).lookupMethod(newMethod) == null;
- lensBuilder.move(method, encodedMethod.isStatic(), newMethod, true);
+ lensBuilder.move(method, newMethod, encodedMethod.isStatic(), true);
encodedMethod.accessFlags.promoteToPublic();
encodedMethod.accessFlags.promoteToStatic();
encodedMethod.clearAnnotations();
@@ -131,7 +131,7 @@
int numberOfExtraNullParameters = newMethod.getArity() - encodedMethod.method.getArity();
boolean isStatic = encodedMethod.isStatic();
lensBuilder.move(
- encodedMethod.method, isStatic, newMethod, isStatic, numberOfExtraNullParameters);
+ encodedMethod.method, newMethod, isStatic, isStatic, numberOfExtraNullParameters);
DexEncodedMethod newEncodedMethod = encodedMethod.toTypeSubstitutedMethod(newMethod);
assert !encodedMethod.isLibraryMethodOverride().isTrue()
: "Enum unboxing is changing the signature of a library override in a non unboxed class.";
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java
index 16c9118..b199c47 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.ProgramPackage;
import com.android.tools.r8.graph.ProgramPackageCollection;
import com.android.tools.r8.origin.SynthesizedOrigin;
@@ -121,6 +122,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
+ ClassSignature.NO_CLASS_SIGNATURE,
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
index 5ddc8dd..92f0972 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroupClassBuilder.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.EnclosingMethodAttribute;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.origin.SynthesizedOrigin;
@@ -50,7 +51,8 @@
Collections.emptyList(),
buildEnclosingMethodAttribute(),
buildInnerClasses(),
- buildAnnotations(),
+ buildClassSignature(),
+ DexAnnotationSet.empty(),
buildStaticFields(appView, feedback),
buildInstanceFields(),
buildDirectMethods(),
@@ -68,7 +70,7 @@
protected abstract List<InnerClassAttribute> buildInnerClasses();
- protected abstract DexAnnotationSet buildAnnotations();
+ protected abstract ClassSignature buildClassSignature();
protected abstract DexEncodedMethod[] buildVirtualMethods();
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 5b0b0b4..db7f56c 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
@@ -10,7 +10,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -23,6 +22,8 @@
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DexValue.DexValueNull;
import com.android.tools.r8.graph.EnclosingMethodAttribute;
+import com.android.tools.r8.graph.GenericSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
@@ -33,6 +34,7 @@
import com.android.tools.r8.ir.optimize.lambda.LambdaGroupClassBuilder;
import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import com.android.tools.r8.ir.synthetic.SyntheticSourceCode;
+import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.IntBox;
import com.android.tools.r8.utils.InternalOptions;
@@ -84,14 +86,11 @@
}
@Override
- protected DexAnnotationSet buildAnnotations() {
+ protected ClassSignature buildClassSignature() {
// Kotlin-style lambdas supported by the merged may only contain optional signature and
// kotlin metadata annotations. We remove the latter, but keep the signature if present.
- String signature = id.signature;
- return signature == null
- ? DexAnnotationSet.empty()
- : new DexAnnotationSet(
- new DexAnnotation[]{DexAnnotation.createSignatureAnnotation(signature, factory)});
+ return GenericSignature.parseClassSignature(
+ origin, id.signature, new SynthesizedOrigin(origin, getClass()), factory, options.reporter);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
index 049748f..5a90f80 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
@@ -91,9 +91,6 @@
public static boolean hasValidAnnotations(Kotlin kotlin, DexClass lambda) {
for (DexAnnotation annotation : lambda.annotations().annotations) {
- if (DexAnnotation.isSignatureAnnotation(annotation, kotlin.factory)) {
- continue;
- }
if (annotation.annotation.type == kotlin.factory.kotlinMetadataType) {
continue;
}
@@ -104,13 +101,7 @@
String validateAnnotations(AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
throws LambdaStructureError {
- String signature = null;
for (DexAnnotation annotation : lambda.liveAnnotations(appView).annotations) {
- if (DexAnnotation.isSignatureAnnotation(annotation, kotlin.factory)) {
- signature = DexAnnotation.getSignature(annotation);
- continue;
- }
-
if (annotation.annotation.type == appView.dexItemFactory().kotlinMetadataType) {
// Ignore kotlin metadata on lambda classes. Metadata on synthetic
// classes exists but is not used in the current Kotlin version (1.2.21)
@@ -123,7 +114,7 @@
"unexpected annotation: " + annotation.annotation.type.toSourceString());
}
assert hasValidAnnotations(kotlin, lambda);
- return signature;
+ return lambda.getClassSignature().toString();
}
void validateStaticFields(Kotlin kotlin, DexClass lambda) throws LambdaStructureError {
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 e28734b..06e3679 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -46,11 +46,13 @@
import com.android.tools.r8.utils.AsmUtils;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.PredicateUtils;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Optional;
+import java.util.function.Predicate;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassTooLargeException;
@@ -79,6 +81,7 @@
private final NamingLens namingLens;
private final InternalOptions options;
private final Marker marker;
+ private final Predicate<DexType> isTypeMissing;
public final ProguardMapSupplier proguardMapSupplier;
@@ -96,6 +99,8 @@
assert marker != null;
this.marker = marker;
this.proguardMapSupplier = proguardMapSupplier;
+ this.isTypeMissing =
+ PredicateUtils.isNull(appView.appInfo()::definitionForWithoutExistenceAssert);
}
public void write(ClassFileConsumer consumer) {
@@ -179,7 +184,7 @@
}
String desc = namingLens.lookupDescriptor(clazz.type).toString();
String name = namingLens.lookupInternalName(clazz.type);
- String signature = getSignature(clazz.annotations());
+ String signature = clazz.getClassSignature().toRenamedString(namingLens, isTypeMissing);
String superName =
clazz.type == options.itemFactory.objectType
? null
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 6f5a765..40713b8 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -32,8 +32,6 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
import java.util.function.Predicate;
class ClassNameMinifier {
@@ -114,14 +112,11 @@
}
}
- ClassRenaming computeRenaming(Timing timing, ExecutorService executorService)
- throws ExecutionException {
- return computeRenaming(timing, executorService, Collections.emptyMap());
+ ClassRenaming computeRenaming(Timing timing) {
+ return computeRenaming(timing, Collections.emptyMap());
}
- ClassRenaming computeRenaming(
- Timing timing, ExecutorService executorService, Map<DexType, DexString> syntheticClasses)
- throws ExecutionException {
+ ClassRenaming computeRenaming(Timing timing, Map<DexType, DexString> syntheticClasses) {
// Externally defined synthetic classes populate an initial renaming.
renaming.putAll(syntheticClasses);
@@ -159,10 +154,6 @@
}
timing.end();
- timing.begin("rename-arrays");
- appView.dexItemFactory().forAllTypes(this::renameArrayTypeIfNeeded);
- timing.end();
-
return new ClassRenaming(Collections.unmodifiableMap(renaming), getPackageRenaming());
}
@@ -253,7 +244,7 @@
if (attribute == null) {
return null;
}
- return attribute.getLiveContext(appView.appInfo());
+ return attribute.getLiveContext(appView);
}
private DexString computeName(DexType type) {
@@ -359,23 +350,6 @@
return state;
}
- private void renameArrayTypeIfNeeded(DexType type) {
- if (type.isArrayType()) {
- DexType base = type.toBaseType(appView.dexItemFactory());
- DexString value = renaming.get(base);
- if (value != null) {
- int dimensions = type.descriptor.numberOfLeadingSquareBrackets();
- StringBuilder builder = new StringBuilder();
- for (int i = 0; i < dimensions; i++) {
- builder.append('[');
- }
- builder.append(value.toString());
- DexString descriptor = appView.dexItemFactory().createString(builder.toString());
- renaming.put(type, descriptor);
- }
- }
- }
-
protected class Namespace implements InternalNamingState {
private final String packageName;
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
index 42a9287..b7bfcdb 100644
--- a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.naming.ClassNameMinifier.ClassRenaming;
import com.android.tools.r8.naming.FieldNameMinifier.FieldRenaming;
import com.android.tools.r8.naming.MethodNameMinifier.MethodRenaming;
+import com.android.tools.r8.naming.NamingLens.NonIdentityNamingLens;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableMap;
@@ -27,7 +28,7 @@
import java.util.function.Function;
import java.util.function.Predicate;
-class MinifiedRenaming extends NamingLens {
+class MinifiedRenaming extends NonIdentityNamingLens {
final AppView<? extends AppInfoWithClassHierarchy> appView;
private final Map<String, String> packageRenaming;
@@ -38,6 +39,7 @@
ClassRenaming classRenaming,
MethodRenaming methodRenaming,
FieldRenaming fieldRenaming) {
+ super(appView.dexItemFactory());
this.appView = appView;
this.packageRenaming = classRenaming.packageRenaming;
renaming.putAll(classRenaming.classRenaming);
@@ -51,7 +53,7 @@
}
@Override
- public DexString lookupDescriptor(DexType type) {
+ protected DexString internalLookupClassDescriptor(DexType type) {
return renaming.getOrDefault(type, type.descriptor);
}
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index 3d27387..b886f2b 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -58,7 +58,7 @@
new MinificationPackageNamingStrategy(appView),
// Use deterministic class order to make sure renaming is deterministic.
appView.appInfo().classesWithDeterministicOrder());
- ClassRenaming classRenaming = classNameMinifier.computeRenaming(timing, executorService);
+ ClassRenaming classRenaming = classNameMinifier.computeRenaming(timing);
timing.end();
assert new MinifiedRenaming(
diff --git a/src/main/java/com/android/tools/r8/naming/NamingLens.java b/src/main/java/com/android/tools/r8/naming/NamingLens.java
index 77b5b12..9147b84 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingLens.java
@@ -45,6 +45,13 @@
public abstract DexString lookupDescriptor(DexType type);
+ public DexString lookupClassDescriptor(DexType type) {
+ assert type.isClassType();
+ return internalLookupClassDescriptor(type);
+ }
+
+ protected abstract DexString internalLookupClassDescriptor(DexType type);
+
public abstract DexString lookupInnerName(InnerClassAttribute attribute, InternalOptions options);
public abstract DexString lookupName(DexMethod method);
@@ -116,7 +123,7 @@
return type.replaceBaseType(newBaseType, dexItemFactory);
}
assert type.isClassType();
- return dexItemFactory.createType(lookupDescriptor(type));
+ return dexItemFactory.createType(lookupClassDescriptor(type));
}
public boolean hasPrefixRewritingLogic() {
@@ -179,7 +186,34 @@
return true;
}
- private static class IdentityLens extends NamingLens {
+ public abstract static class NonIdentityNamingLens extends NamingLens {
+
+ private final DexItemFactory dexItemFactory;
+
+ protected NonIdentityNamingLens(DexItemFactory dexItemFactory) {
+ this.dexItemFactory = dexItemFactory;
+ }
+
+ protected DexItemFactory dexItemFactory() {
+ return dexItemFactory;
+ }
+
+ @Override
+ public final DexString lookupDescriptor(DexType type) {
+ if (type.isPrimitiveType() || type.isVoidType() || type.isNullValueType()) {
+ return type.getDescriptor();
+ }
+ if (type.isArrayType()) {
+ DexType baseType = type.toBaseType(dexItemFactory);
+ DexString desc = lookupDescriptor(baseType);
+ return desc.toArrayDescriptor(type.getNumberOfLeadingSquareBrackets(), dexItemFactory);
+ }
+ assert type.isClassType();
+ return lookupClassDescriptor(type);
+ }
+ }
+
+ private static final class IdentityLens extends NamingLens {
private IdentityLens() {
// Intentionally left empty.
@@ -191,6 +225,11 @@
}
@Override
+ protected DexString internalLookupClassDescriptor(DexType type) {
+ return type.descriptor;
+ }
+
+ @Override
public DexString lookupInnerName(InnerClassAttribute attribute, InternalOptions options) {
return attribute.getInnerName();
}
diff --git a/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java b/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
index a1451fc..e987def 100644
--- a/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.naming.NamingLens.NonIdentityNamingLens;
import com.android.tools.r8.utils.InternalOptions;
import java.util.Collections;
import java.util.HashMap;
@@ -21,7 +22,7 @@
import java.util.stream.Stream;
// Naming lens for rewriting type prefixes.
-public class PrefixRewritingNamingLens extends NamingLens {
+public class PrefixRewritingNamingLens extends NonIdentityNamingLens {
final NamingLens namingLens;
final InternalOptions options;
@@ -40,6 +41,7 @@
}
public PrefixRewritingNamingLens(NamingLens namingLens, AppView<?> appView) {
+ super(appView.dexItemFactory());
this.appView = appView;
this.namingLens = namingLens;
this.options = appView.options();
@@ -68,7 +70,7 @@
}
@Override
- public DexString lookupDescriptor(DexType type) {
+ protected DexString internalLookupClassDescriptor(DexType type) {
DexString renaming = getRenaming(type);
return renaming != null ? renaming : namingLens.lookupDescriptor(type);
}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index a29f8ef..fab1147 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -143,7 +143,7 @@
new MinificationPackageNamingStrategy(appView),
mappedClasses);
ClassRenaming classRenaming =
- classNameMinifier.computeRenaming(timing, executorService, syntheticCompanionClasses);
+ classNameMinifier.computeRenaming(timing, syntheticCompanionClasses);
timing.end();
ApplyMappingMemberNamingStrategy nameStrategy =
@@ -578,9 +578,9 @@
}
@Override
- public DexString lookupDescriptor(DexType type) {
+ protected DexString internalLookupClassDescriptor(DexType type) {
checkForUseOfNotMappedReference(type);
- return super.lookupDescriptor(type);
+ return super.internalLookupClassDescriptor(type);
}
private void checkForUseOfNotMappedReference(DexType type) {
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 92b4402..e1a49d8 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
@@ -13,6 +13,8 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignatureTypeRewriter;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -27,7 +29,7 @@
import java.util.function.Predicate;
import java.util.function.Supplier;
-// TODO(b/129925954): Reimplement this by using the internal encoding and transformation logic.
+// TODO(b/169516860): We should generalize this to handle rewriting of attributes in general.
public class GenericSignatureRewriter {
private final AppView<?> appView;
@@ -56,17 +58,27 @@
ThreadUtils.processItems(
classes,
clazz -> {
+ GenericSignatureTypeRewriter genericSignatureTypeRewriter =
+ new GenericSignatureTypeRewriter(appView, clazz);
GenericSignatureCollector genericSignatureCollector =
new GenericSignatureCollector(clazz);
GenericSignatureParser<DexType> genericSignatureParser =
new GenericSignatureParser<>(genericSignatureCollector);
- clazz.setAnnotations(
- rewriteGenericSignatures(
- clazz.annotations(),
- genericSignatureParser::parseClassSignature,
- genericSignatureCollector::getRenamedSignature,
- (signature, e) ->
- options.warningInvalidSignature(clazz, clazz.getOrigin(), signature, e)));
+ ClassSignature classSignature = clazz.getClassSignature();
+ if (classSignature.hasSignature()) {
+ // TODO(b/129925954): We still have to rewrite to capture the lastWrittenType.
+ // The design is utterly broken.
+ DexAnnotation classSignatureAnnotation =
+ DexAnnotation.createSignatureAnnotation(
+ classSignature.toString(), options.itemFactory);
+ rewriteGenericSignatures(
+ new DexAnnotationSet(new DexAnnotation[] {classSignatureAnnotation}),
+ genericSignatureParser::parseClassSignature,
+ genericSignatureCollector::getRenamedSignature,
+ (signature, e) ->
+ options.warningInvalidSignature(clazz, clazz.getOrigin(), signature, e));
+ }
+ clazz.setClassSignature(genericSignatureTypeRewriter.rewrite(classSignature));
clazz.forEachField(
field ->
field.setAnnotations(
@@ -91,6 +103,7 @@
executorService);
}
+ // TODO(b/129925954): Remove this when using modeled signatures for methods and fields.
private DexAnnotationSet rewriteGenericSignatures(
DexAnnotationSet annotations,
Consumer<String> parser,
diff --git a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
index 4edec01..55c58c6cd 100644
--- a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
@@ -11,16 +11,20 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
+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.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.ir.optimize.MethodPoolCollection;
import com.android.tools.r8.optimize.PublicizerLens.PublicizedLensBuilder;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.Timing;
import java.util.LinkedHashSet;
@@ -32,6 +36,7 @@
private final DexApplication application;
private final AppView<AppInfoWithLiveness> appView;
+ private final KeepInfoCollection keepInfo;
private final SubtypingInfo subtypingInfo;
private final MethodPoolCollection methodPoolCollection;
@@ -43,6 +48,7 @@
SubtypingInfo subtypingInfo) {
this.application = application;
this.appView = appView;
+ this.keepInfo = appView.appInfo().getKeepInfo();
this.subtypingInfo = subtypingInfo;
this.methodPoolCollection =
// We will add private instance methods when we promote them.
@@ -82,6 +88,12 @@
return lensBuilder.build(appView);
}
+ private void doPublicize(ProgramDefinition definition) {
+ definition.getAccessFlags().promoteToPublic();
+ keepInfo.mutate(
+ keepInfo -> keepInfo.unsetRequireAllowAccessModificationForRepackaging(definition));
+ }
+
private void publicizeType(DexType type) {
DexProgramClass clazz = asProgramClassOrNull(application.definitionFor(type));
if (clazz != null) {
@@ -91,30 +103,31 @@
}
private void publicizeClass(DexProgramClass clazz) {
- clazz.accessFlags.promoteToPublic();
+ doPublicize(clazz);
// Publicize fields.
- clazz.forEachField(
+ clazz.forEachProgramField(
field -> {
- if (field.isPublic()) {
+ DexEncodedField definition = field.getDefinition();
+ if (definition.isPublic()) {
return;
}
- if (!appView.appInfo().isAccessModificationAllowed(field.field)) {
+ if (!appView.appInfo().isAccessModificationAllowed(field.getReference())) {
// TODO(b/131130038): Also do not publicize package-private and protected fields that
// are kept.
- if (field.isPrivate()) {
+ if (definition.isPrivate()) {
return;
}
}
- field.accessFlags.promoteToPublic();
+ doPublicize(field);
});
// Publicize methods.
Set<DexEncodedMethod> privateInstanceMethods = new LinkedHashSet<>();
- clazz.forEachMethod(
+ clazz.forEachProgramMethod(
method -> {
- if (publicizeMethod(clazz, method)) {
- privateInstanceMethods.add(method);
+ if (publicizeMethod(method)) {
+ privateInstanceMethods.add(method.getDefinition());
}
});
if (!privateInstanceMethods.isEmpty()) {
@@ -131,58 +144,58 @@
}
}
- private boolean publicizeMethod(DexProgramClass holder, DexEncodedMethod method) {
- MethodAccessFlags accessFlags = method.accessFlags;
+ private boolean publicizeMethod(ProgramMethod method) {
+ MethodAccessFlags accessFlags = method.getAccessFlags();
if (accessFlags.isPublic()) {
return false;
}
// If this method is mentioned in keep rules, do not transform (rule applications changed).
- if (!appView.appInfo().isAccessModificationAllowed(method.method)) {
+ DexEncodedMethod definition = method.getDefinition();
+ if (!appView.appInfo().isAccessModificationAllowed(method.getReference())) {
// TODO(b/131130038): Also do not publicize package-private and protected methods that are
// kept.
- if (method.isPrivate()) {
+ if (definition.isPrivate()) {
return false;
}
}
- if (!accessFlags.isPrivate() || appView.dexItemFactory().isConstructor(method.method)) {
+ if (!accessFlags.isPrivate() || appView.dexItemFactory().isConstructor(method.getReference())) {
// TODO(b/150589374): This should check for dispatch targets or just abandon in
// package-private.
- accessFlags.promoteToPublic();
+ doPublicize(method);
return false;
}
- if (!accessFlags.isStatic()) {
-
- // We can't publicize private instance methods in interfaces or methods that are copied from
- // interfaces to lambda-desugared classes because this will be added as a new default method.
- // TODO(b/111118390): It might be possible to transform it into static methods, though.
- if (holder.isInterface() || accessFlags.isSynthetic()) {
- return false;
- }
-
- boolean wasSeen = methodPoolCollection.markIfNotSeen(holder, method.method);
- if (wasSeen) {
- // We can't do anything further because even renaming is not allowed due to the keep rule.
- if (!appView.appInfo().isMinificationAllowed(method.method)) {
- return false;
- }
- // TODO(b/111118390): Renaming will enable more private instance methods to be publicized.
- return false;
- }
- lensBuilder.add(method.method);
- accessFlags.promoteToFinal();
- accessFlags.promoteToPublic();
- // The method just became public and is therefore not a library override.
- method.setLibraryMethodOverride(OptionalBool.FALSE);
- return true;
+ if (accessFlags.isStatic()) {
+ // For private static methods we can just relax the access to public, since
+ // even though JLS prevents from declaring static method in derived class if
+ // an instance method with same signature exists in superclass, JVM actually
+ // does not take into account access of the static methods.
+ doPublicize(method);
+ return false;
}
- // For private static methods we can just relax the access to public, since
- // even though JLS prevents from declaring static method in derived class if
- // an instance method with same signature exists in superclass, JVM actually
- // does not take into account access of the static methods.
- accessFlags.promoteToPublic();
- return false;
+ // We can't publicize private instance methods in interfaces or methods that are copied from
+ // interfaces to lambda-desugared classes because this will be added as a new default method.
+ // TODO(b/111118390): It might be possible to transform it into static methods, though.
+ if (method.getHolder().isInterface() || accessFlags.isSynthetic()) {
+ return false;
+ }
+
+ boolean wasSeen = methodPoolCollection.markIfNotSeen(method.getHolder(), method.getReference());
+ if (wasSeen) {
+ // We can't do anything further because even renaming is not allowed due to the keep rule.
+ if (!appView.appInfo().isMinificationAllowed(method.getReference())) {
+ return false;
+ }
+ // TODO(b/111118390): Renaming will enable more private instance methods to be publicized.
+ return false;
+ }
+ lensBuilder.add(method.getReference());
+ accessFlags.promoteToFinal();
+ doPublicize(method);
+ // The method just became public and is therefore not a library override.
+ definition.setLibraryMethodOverride(OptionalBool.FALSE);
+ return true;
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingUtils.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingUtils.java
new file mode 100644
index 0000000..5a9e20c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingUtils.java
@@ -0,0 +1,29 @@
+// 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.optimize;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
+
+public class MemberRebindingUtils {
+
+ public static boolean isNonReboundMethodReference(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ DexMethod method,
+ ProgramMethod context) {
+ DexClass clazz = appView.definitionForHolder(method, context);
+ if (clazz == null) {
+ return false;
+ }
+ SingleResolutionResult resolutionResult =
+ appView.appInfo().resolveMethodOn(clazz, method).asSingleResolution();
+ return resolutionResult != null
+ && resolutionResult.getResolvedHolder().getType() != method.getHolderType();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/BridgeHoisting.java b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
similarity index 82%
rename from src/main/java/com/android/tools/r8/optimize/BridgeHoisting.java
rename to src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
index 4dc644c..0d2c4e8 100644
--- a/src/main/java/com/android/tools/r8/optimize/BridgeHoisting.java
+++ b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
@@ -1,7 +1,7 @@
// 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.optimize;
+package com.android.tools.r8.optimize.bridgehoisting;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
@@ -20,7 +20,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
+import com.android.tools.r8.graph.MethodAccessInfoCollection;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.SubtypingInfo;
@@ -31,9 +31,6 @@
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.google.common.base.Equivalence;
import com.google.common.base.Equivalence.Wrapper;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
-import com.google.common.collect.ImmutableMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -73,11 +70,13 @@
private final AppView<AppInfoWithLiveness> appView;
- // A lens that keeps track of the changes for construction of the Proguard map.
- private final BridgeHoistingLens.Builder lensBuilder = new BridgeHoistingLens.Builder();
+ // Structure that keeps track of the changes for construction of the Proguard map and
+ // AppInfoWithLiveness maintenance.
+ private final BridgeHoistingResult result;
public BridgeHoisting(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
+ this.result = new BridgeHoistingResult(appView);
}
public void run() {
@@ -85,9 +84,30 @@
BottomUpClassHierarchyTraversal.forProgramClasses(appView, subtypingInfo)
.excludeInterfaces()
.visit(appView.appInfo().classes(), clazz -> processClass(clazz, subtypingInfo));
- if (!lensBuilder.isEmpty()) {
- BridgeHoistingLens lens = lensBuilder.build(appView);
+ if (!result.isEmpty()) {
+ BridgeHoistingLens lens = result.buildLens();
appView.rewriteWithLens(lens);
+
+ // Update method access info collection.
+ MethodAccessInfoCollection.Modifier methodAccessInfoCollectionModifier =
+ appView.appInfo().getMethodAccessInfoCollection().modifier();
+
+ // The bridge hoisting lens does not specify any code rewritings. Therefore references to the
+ // bridge methods are left as-is in the code, but they are rewritten to the hoisted bridges
+ // during the rewriting of AppInfoWithLiveness. Therefore, this conservatively records that
+ // there may be an invoke-virtual instruction that targets each of the removed bridges.
+ methodAccessInfoCollectionModifier.addAll(result.getBridgeMethodAccessInfoCollection());
+
+ // Additionally, we record the invokes from the newly synthesized bridge methods.
+ result.forEachHoistedBridge(
+ (bridge, bridgeInfo) -> {
+ if (bridgeInfo.isVirtualBridgeInfo()) {
+ DexMethod reference = bridgeInfo.asVirtualBridgeInfo().getInvokedMethod();
+ methodAccessInfoCollectionModifier.registerInvokeVirtualInContext(reference, bridge);
+ } else {
+ assert false;
+ }
+ });
}
}
@@ -120,40 +140,6 @@
return candidates;
}
- /**
- * Returns true if the bridge method is referencing a method in the superclass of {@param holder}.
- * If this is not the case, we cannot hoist the bridge method, as that would lead to a type error:
- * <code>
- * class A {
- * void bridge() {
- * v0 <- Argument
- * invoke-virtual {v0}, void B.m() // <- not valid
- * Return
- * }
- * }
- * class B extends A {
- * void m() {
- * ...
- * }
- * }
- * </code>
- */
- private boolean bridgeIsTargetingMethodInSuperclass(
- DexProgramClass holder, BridgeInfo bridgeInfo) {
- if (bridgeInfo.isVirtualBridgeInfo()) {
- VirtualBridgeInfo virtualBridgeInfo = bridgeInfo.asVirtualBridgeInfo();
- DexMethod invokedMethod = virtualBridgeInfo.getInvokedMethod();
- assert !appView.appInfo().isStrictSubtypeOf(invokedMethod.holder, holder.type);
- if (invokedMethod.holder == holder.type) {
- return false;
- }
- assert appView.appInfo().isStrictSubtypeOf(holder.type, invokedMethod.holder);
- return true;
- }
- assert false;
- return false;
- }
-
private void hoistBridgeIfPossible(
DexMethod method, DexProgramClass clazz, Set<DexProgramClass> subclasses) {
// If the method is defined on the parent class, we cannot hoist the bridge.
@@ -251,7 +237,7 @@
newMethod.getAccessFlags().demoteFromFinal();
}
clazz.addVirtualMethod(newMethod);
- lensBuilder.move(representative.getReference(), newMethodReference);
+ result.move(representative.getReference(), newMethodReference);
// Remove all of the bridges in the eligible subclasses.
for (DexProgramClass subclass : eligibleSubclasses) {
@@ -366,47 +352,4 @@
code.getDebugInfo())
: code;
}
-
- static class BridgeHoistingLens extends NestedGraphLens {
-
- public BridgeHoistingLens(
- AppView<?> appView, BiMap<DexMethod, DexMethod> originalMethodSignatures) {
- super(
- ImmutableMap.of(),
- ImmutableMap.of(),
- ImmutableMap.of(),
- null,
- originalMethodSignatures,
- appView.graphLens(),
- appView.dexItemFactory());
- }
-
- @Override
- public boolean hasCodeRewritings() {
- return getPrevious().hasCodeRewritings();
- }
-
- @Override
- public boolean isLegitimateToHaveEmptyMappings() {
- return true;
- }
-
- static class Builder {
-
- private final BiMap<DexMethod, DexMethod> originalMethodSignatures = HashBiMap.create();
-
- public boolean isEmpty() {
- return originalMethodSignatures.isEmpty();
- }
-
- public void move(DexMethod from, DexMethod to) {
- originalMethodSignatures.forcePut(to, originalMethodSignatures.getOrDefault(from, from));
- }
-
- public BridgeHoistingLens build(AppView<?> appView) {
- assert !isEmpty();
- return new BridgeHoistingLens(appView, originalMethodSignatures);
- }
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java
new file mode 100644
index 0000000..9fe3e29
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingLens.java
@@ -0,0 +1,91 @@
+// 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.optimize.bridgehoisting;
+
+import com.android.tools.r8.graph.AppView;
+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.graph.GraphLens;
+import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
+import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
+import java.util.Set;
+
+class BridgeHoistingLens extends NonIdentityGraphLens {
+
+ // Mapping from non-hoisted bridge methods to hoisted bridge methods.
+ private final BidirectionalManyToOneMap<DexMethod, DexMethod> bridgeToHoistedBridgeMap;
+
+ public BridgeHoistingLens(
+ AppView<?> appView,
+ BidirectionalManyToOneMap<DexMethod, DexMethod> bridgeToHoistedBridgeMap) {
+ super(appView.dexItemFactory(), appView.graphLens());
+ this.bridgeToHoistedBridgeMap = bridgeToHoistedBridgeMap;
+ }
+
+ @Override
+ public DexMethod getOriginalMethodSignature(DexMethod method) {
+ return getPrevious().getOriginalMethodSignature(internalGetPreviousMethodSignature(method));
+ }
+
+ @Override
+ public DexMethod getRenamedMethodSignature(DexMethod originalMethod, GraphLens applied) {
+ DexMethod renamedMethod = getPrevious().getRenamedMethodSignature(originalMethod, applied);
+ return bridgeToHoistedBridgeMap.getOrDefault(renamedMethod, renamedMethod);
+ }
+
+ @Override
+ protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
+ Set<DexMethod> bridges = bridgeToHoistedBridgeMap.getKeys(method);
+ return bridges.isEmpty() ? method : bridges.iterator().next();
+ }
+
+ @Override
+ public DexType getOriginalType(DexType type) {
+ return getPrevious().getOriginalType(type);
+ }
+
+ @Override
+ public DexField getOriginalFieldSignature(DexField field) {
+ return getPrevious().getOriginalFieldSignature(field);
+ }
+
+ @Override
+ public DexField getRenamedFieldSignature(DexField originalField) {
+ return getPrevious().getRenamedFieldSignature(originalField);
+ }
+
+ @Override
+ public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(DexMethod method) {
+ return getPrevious().lookupPrototypeChangesForMethodDefinition(method);
+ }
+
+ @Override
+ public boolean isContextFreeForMethods() {
+ return getPrevious().isContextFreeForMethods();
+ }
+
+ @Override
+ public boolean hasCodeRewritings() {
+ return getPrevious().hasCodeRewritings();
+ }
+
+ @Override
+ protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
+ return previous;
+ }
+
+ @Override
+ protected MethodLookupResult internalDescribeLookupMethod(
+ MethodLookupResult previous, DexMethod context) {
+ return previous;
+ }
+
+ @Override
+ protected DexType internalDescribeLookupClassType(DexType previous) {
+ return previous;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingResult.java b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingResult.java
new file mode 100644
index 0000000..09a9b27
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingResult.java
@@ -0,0 +1,75 @@
+// 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.optimize.bridgehoisting;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.MethodAccessInfoCollection;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
+import java.util.Set;
+import java.util.function.BiConsumer;
+
+class BridgeHoistingResult {
+
+ private final AppView<AppInfoWithLiveness> appView;
+
+ // Mapping from non-hoisted bridge methods to hoisted bridge methods.
+ private final BidirectionalManyToOneMap<DexMethod, DexMethod> bridgeToHoistedBridgeMap =
+ new BidirectionalManyToOneMap<>();
+
+ // Mapping from non-hoisted bridge methods to the set of contexts in which they are accessed.
+ private final MethodAccessInfoCollection.IdentityBuilder bridgeMethodAccessInfoCollectionBuilder =
+ MethodAccessInfoCollection.identityBuilder();
+
+ BridgeHoistingResult(AppView<AppInfoWithLiveness> appView) {
+ this.appView = appView;
+ }
+
+ public void forEachHoistedBridge(BiConsumer<ProgramMethod, BridgeInfo> consumer) {
+ bridgeToHoistedBridgeMap.forEach(
+ (bridges, hoistedBridge) -> {
+ DexProgramClass clazz = appView.definitionForProgramType(hoistedBridge.getHolderType());
+ ProgramMethod method = hoistedBridge.lookupOnProgramClass(clazz);
+ if (method != null) {
+ consumer.accept(method, method.getDefinition().getOptimizationInfo().getBridgeInfo());
+ }
+ });
+ }
+
+ public MethodAccessInfoCollection getBridgeMethodAccessInfoCollection() {
+ return bridgeMethodAccessInfoCollectionBuilder.build();
+ }
+
+ public boolean isEmpty() {
+ return bridgeToHoistedBridgeMap.isEmpty();
+ }
+
+ public void move(DexMethod from, DexMethod to) {
+ Set<DexMethod> keys = bridgeToHoistedBridgeMap.getKeys(from);
+ if (keys.isEmpty()) {
+ bridgeToHoistedBridgeMap.put(from, to);
+ } else {
+ for (DexMethod original : keys) {
+ bridgeToHoistedBridgeMap.put(original, to);
+ }
+ }
+
+ MethodAccessInfoCollection methodAccessInfoCollection =
+ appView.appInfo().getMethodAccessInfoCollection();
+ methodAccessInfoCollection.forEachVirtualInvokeContext(
+ from,
+ context ->
+ bridgeMethodAccessInfoCollectionBuilder.registerInvokeVirtualInContext(from, context));
+ }
+
+ public BridgeHoistingLens buildLens() {
+ assert !isEmpty();
+ return new BridgeHoistingLens(appView, bridgeToHoistedBridgeMap);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/relocator/SimplePackagesRewritingMapper.java b/src/main/java/com/android/tools/r8/relocator/SimplePackagesRewritingMapper.java
index 50a6e69..09ca5bd 100644
--- a/src/main/java/com/android/tools/r8/relocator/SimplePackagesRewritingMapper.java
+++ b/src/main/java/com/android/tools/r8/relocator/SimplePackagesRewritingMapper.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.naming.NamingLens.NonIdentityNamingLens;
import com.android.tools.r8.references.PackageReference;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -95,23 +96,18 @@
return new RelocatorNamingLens(typeMappings, packingMappings.build(), appView.dexItemFactory());
}
- Map<DexType, DexString> getTypeMappings() {
- return typeMappings;
- }
-
- private static class RelocatorNamingLens extends NamingLens {
+ private static class RelocatorNamingLens extends NonIdentityNamingLens {
private final Map<DexType, DexString> typeMappings;
private final Map<String, String> packageMappings;
- private final DexItemFactory factory;
private RelocatorNamingLens(
Map<DexType, DexString> typeMappings,
Map<String, String> packageMappings,
DexItemFactory factory) {
+ super(factory);
this.typeMappings = typeMappings;
this.packageMappings = packageMappings;
- this.factory = factory;
}
@Override
@@ -120,20 +116,7 @@
}
@Override
- public DexString lookupDescriptor(DexType type) {
- if (type.isPrimitiveType() || type.isVoidType()) {
- return type.descriptor;
- }
- if (type.isArrayType()) {
- DexType baseType = type.toBaseType(factory);
- if (baseType == null || baseType.isPrimitiveType()) {
- return type.descriptor;
- }
- String baseDescriptor = typeMappings.getOrDefault(baseType, baseType.descriptor).toString();
- return factory.createString(
- DescriptorUtils.toArrayDescriptor(
- type.getNumberOfLeadingSquareBrackets(), baseDescriptor));
- }
+ protected DexString internalLookupClassDescriptor(DexType type) {
return typeMappings.getOrDefault(type, type.descriptor);
}
diff --git a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
index 95ee828..79b2e1e 100644
--- a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
+++ b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
@@ -96,7 +96,7 @@
repackagingConfiguration.getNewPackageDescriptor(pkg, seenPackageDescriptors);
if (pkg.getPackageDescriptor().equals(newPackageDescriptor)) {
for (DexProgramClass alreadyRepackagedClass : pkg) {
- if (!appView.appInfo().isRepackagingAllowed(alreadyRepackagedClass.getType())) {
+ if (!appView.appInfo().isRepackagingAllowed(alreadyRepackagedClass)) {
mappings.put(alreadyRepackagedClass.getType(), alreadyRepackagedClass.getType());
}
}
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
index fbaf1b8..b4760c1 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingConstraintGraph.java
@@ -54,7 +54,7 @@
// package descriptor.
boolean hasPinnedItem = false;
for (DexProgramClass clazz : pkg) {
- boolean isPinned = !appView.appInfo().isRepackagingAllowed(clazz.getType());
+ boolean isPinned = !appView.appInfo().isRepackagingAllowed(clazz);
Node classNode = createNode(clazz);
if (isPinned) {
pinnedNodes.add(classNode);
@@ -136,6 +136,14 @@
// Trace the type references in the method signature.
definition.getProto().forEachType(registry::registerTypeReference);
+ // Check if this overrides a package-private method.
+ DexProgramClass superClass =
+ appView.programDefinitionFor(method.getHolder().getSuperType(), method.getHolder());
+ if (superClass != null) {
+ registry.registerMemberAccess(
+ appView.appInfo().resolveMethodOn(superClass, method.getReference()));
+ }
+
// Trace the references in the method and method parameter annotations.
RepackagingAnnotationTracer annotationTracer =
new RepackagingAnnotationTracer(appView, registry);
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java
index f519f01..8820838 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java
@@ -69,10 +69,11 @@
fixupType(clazz.superType),
fixupTypeList(clazz.interfaces),
clazz.getSourceFile(),
- fixupNestHost(clazz.getNestHost()),
+ fixupNestHost(clazz.getNestHostClassAttribute()),
fixupNestMemberAttributes(clazz.getNestMembersClassAttributes()),
fixupEnclosingMethodAttribute(clazz.getEnclosingMethodAttribute()),
fixupInnerClassAttributes(clazz.getInnerClasses()),
+ clazz.getClassSignature(),
clazz.annotations(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
@@ -196,8 +197,10 @@
.createMethod(fixupType(method.holder), fixupProto(method.proto), method.name);
}
- private NestHostClassAttribute fixupNestHost(DexType type) {
- return type != null ? new NestHostClassAttribute(fixupType(type)) : null;
+ private NestHostClassAttribute fixupNestHost(NestHostClassAttribute nestHostClassAttribute) {
+ return nestHostClassAttribute != null
+ ? new NestHostClassAttribute(fixupType(nestHostClassAttribute.getNestHost()))
+ : null;
}
private List<NestMemberClassAttribute> fixupNestMemberAttributes(
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
index b27182d..57361d3 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.EnclosingMethodAttribute;
+import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.MemberResolutionResult;
import com.android.tools.r8.graph.ProgramDefinition;
@@ -32,6 +33,7 @@
private final AppInfoWithLiveness appInfo;
private final RepackagingConstraintGraph constraintGraph;
private final ProgramDefinition context;
+ private final InitClassLens initClassLens;
private final RepackagingConstraintGraph.Node node;
public RepackagingUseRegistry(
@@ -42,6 +44,7 @@
this.appInfo = appView.appInfo();
this.constraintGraph = constraintGraph;
this.context = context;
+ this.initClassLens = appView.initClassLens();
this.node = constraintGraph.getNode(context.getDefinition());
}
@@ -145,7 +148,7 @@
@Override
public void registerInitClass(DexType type) {
- registerTypeAccess(type);
+ registerFieldAccess(initClassLens.getInitClassField(type));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java b/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
index 64cf59a..9b6694f 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.Keep;
-import com.android.tools.r8.utils.StringDiagnostic;
import java.io.IOException;
import java.util.List;
import java.util.function.Consumer;
@@ -136,11 +135,6 @@
if (this.retracedStackTraceConsumer == null) {
throw new RuntimeException("RetracedStackConsumer not specified");
}
- if (isVerbose && regularExpression != null) {
- this.diagnosticsHandler.warning(
- new StringDiagnostic(
- "Retrace does not support verbose output when a regular expression is specified"));
- }
return new RetraceCommand(
isVerbose,
regularExpression,
diff --git a/src/main/java/com/android/tools/r8/retrace/Retracer.java b/src/main/java/com/android/tools/r8/retrace/Retracer.java
index 0f7d547..31619e7 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retracer.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retracer.java
@@ -19,6 +19,7 @@
private Retracer(ClassNameMapper classNameMapper) {
this.classNameMapper = classNameMapper;
+ assert classNameMapper != null;
}
public static RetraceApi create(ClassNameMapper classNameMapper) {
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 1a8d708..4fa3212 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -83,6 +83,9 @@
assert !DexAnnotation.isMemberClassesAnnotation(annotation, dexItemFactory);
assert !DexAnnotation.isEnclosingMethodAnnotation(annotation, dexItemFactory);
assert !DexAnnotation.isEnclosingClassAnnotation(annotation, dexItemFactory);
+ // TODO(b/129925954): Signature is being represented as a class attribute.
+ assert !holder.isDexClass()
+ || !DexAnnotation.isSignatureAnnotation(annotation, dexItemFactory);
if (config.exceptions && DexAnnotation.isThrowingAnnotation(annotation, dexItemFactory)) {
return true;
}
@@ -184,15 +187,6 @@
}
}
- private static boolean hasSignatureAnnotation(DexProgramClass clazz, DexItemFactory itemFactory) {
- for (DexAnnotation annotation : clazz.annotations().annotations) {
- if (DexAnnotation.isSignatureAnnotation(annotation, itemFactory)) {
- return true;
- }
- }
- return false;
- }
-
public void run() {
for (DexProgramClass clazz : appView.appInfo().classes()) {
stripAttributes(clazz);
@@ -291,6 +285,9 @@
hasInnerClassesFromSet(clazz, classesToRetainInnerClassAttributeFor);
}
if (keptAnyway || keepForThisInnerClass || keepForThisEnclosingClass) {
+ if (!keep.signature) {
+ clazz.clearClassSignature();
+ }
if (!keep.enclosingMethod) {
clazz.clearEnclosingMethodAttribute();
}
@@ -325,6 +322,7 @@
// reflection. (Note that clearing these attributes can enable more vertical class merging.)
clazz.clearEnclosingMethodAttribute();
clazz.clearInnerClasses();
+ clazz.clearClassSignature();
}
}
@@ -368,7 +366,7 @@
Map<DexType, DexProgramClass> enclosingClasses = new IdentityHashMap<>();
Set<DexProgramClass> genericClasses = Sets.newIdentityHashSet();
for (DexProgramClass clazz : appView.appInfo().classes()) {
- if (hasSignatureAnnotation(clazz, appView.dexItemFactory())) {
+ if (clazz.getClassSignature().hasSignature()) {
genericClasses.add(clazz);
}
for (InnerClassAttribute innerClassAttribute : clazz.getInnerClasses()) {
@@ -387,7 +385,7 @@
if (appView.appInfo().isNonProgramTypeOrLiveProgramType(inner)) {
result.add(inner);
}
- DexType context = innerClassAttribute.getLiveContext(appView.appInfo());
+ DexType context = innerClassAttribute.getLiveContext(appView);
if (context != null && appView.appInfo().isNonProgramTypeOrLiveProgramType(context)) {
result.add(context);
}
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 acb52c5..b0ee416 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -39,6 +39,7 @@
import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl;
import com.android.tools.r8.graph.PresortedComparable;
+import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.SubtypingInfo;
@@ -477,18 +478,18 @@
DexMethod method = reference.asDexMethod();
DexProgramClass clazz = asProgramClassOrNull(previous.definitionFor(method.holder));
if (clazz != null) {
- DexEncodedMethod definition = clazz.lookupMethod(method);
+ ProgramMethod definition = clazz.lookupProgramMethod(method);
if (definition != null) {
- collection.pinMethod(clazz, definition);
+ collection.pinMethod(definition);
}
}
} else {
DexField field = reference.asDexField();
DexProgramClass clazz = asProgramClassOrNull(previous.definitionFor(field.holder));
if (clazz != null) {
- DexEncodedField definition = clazz.lookupField(field);
+ ProgramField definition = clazz.lookupProgramField(field);
if (definition != null) {
- collection.pinField(clazz, definition);
+ collection.pinField(definition);
}
}
}
@@ -912,9 +913,22 @@
return keepInfo.getInfo(reference, this).isAccessModificationAllowed(options());
}
- public boolean isRepackagingAllowed(DexType type) {
- return options().isRepackagingEnabled()
- && keepInfo.getClassInfo(type, this).isRepackagingAllowed(options());
+ public boolean isRepackagingAllowed(DexProgramClass clazz) {
+ if (!options().isRepackagingEnabled()) {
+ return false;
+ }
+ if (!keepInfo.getInfo(clazz).isRepackagingAllowed(options())) {
+ return false;
+ }
+ return clazz
+ .traverseProgramMethods(
+ member -> {
+ if (keepInfo.getInfo(member).isRepackagingAllowed(options())) {
+ return TraversalContinuation.CONTINUE;
+ }
+ return TraversalContinuation.BREAK;
+ })
+ .shouldContinue();
}
public boolean isPinned(DexReference reference) {
@@ -951,6 +965,7 @@
if (!removedClasses.isEmpty()) {
// Rebuild the hierarchy.
objectAllocationInfoCollection.mutate(mutator -> {}, this);
+ keepInfo.mutate(keepInfo -> keepInfo.removeKeepInfoForPrunedItems(removedClasses));
}
return new AppInfoWithLiveness(this, application, removedClasses, additionalPinnedItems);
}
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 64b1ef0..fc5c0b2 100644
--- a/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
@@ -47,7 +47,7 @@
// Use an existing static field if there is one.
DexEncodedField encodedClinitField = null;
for (DexEncodedField staticField : clazz.staticFields()) {
- // We need to field to be accessible from the contexts in which it is accessed.
+ // We need the field to be accessible from the contexts in which it is accessed.
if (!isMinimumRequiredVisibility(staticField, minimumRequiredVisibility)) {
continue;
}
@@ -56,8 +56,18 @@
if (staticField.field.type.isWideType()) {
continue;
}
- encodedClinitField = staticField;
- break;
+ if (encodedClinitField == null) {
+ encodedClinitField = staticField;
+ } else {
+ // Prefer the field that is most visible.
+ if (staticField.getAccessFlags().getVisibilityOrdinal()
+ > encodedClinitField.getAccessFlags().getVisibilityOrdinal()) {
+ encodedClinitField = staticField;
+ }
+ }
+ if (encodedClinitField.isPublic()) {
+ break;
+ }
}
if (encodedClinitField == null) {
FieldAccessFlags accessFlags =
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 a210ef7..cd02bbb 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -54,6 +54,7 @@
import com.android.tools.r8.graph.MethodAccessInfoCollection;
import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl;
import com.android.tools.r8.graph.PresortedComparable;
+import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult;
@@ -679,7 +680,7 @@
private void enqueueRootField(
ProgramField field, Set<ProguardKeepRuleBase> rules, DexDefinition precondition) {
- keepFieldWithRules(field.getHolder(), field.getDefinition(), rules);
+ keepFieldWithRules(field, rules);
workList.enqueueMarkFieldKeptAction(
field, graphReporter.reportKeepField(precondition, rules, field.getDefinition()));
}
@@ -697,7 +698,7 @@
private void enqueueRootMethod(
ProgramMethod method, Set<ProguardKeepRuleBase> rules, DexDefinition precondition) {
- keepMethodWithRules(method.getHolder(), method.getDefinition(), rules);
+ keepMethodWithRules(method, rules);
workList.enqueueMarkMethodKeptAction(
method, graphReporter.reportKeepMethod(precondition, rules, method.getDefinition()));
}
@@ -2605,13 +2606,13 @@
}
private void markEnumValuesAsReachable(DexProgramClass clazz, KeepReason reason) {
- DexEncodedMethod valuesMethod = clazz.lookupMethod(generatedEnumValuesMethod(clazz));
+ ProgramMethod valuesMethod = clazz.lookupProgramMethod(generatedEnumValuesMethod(clazz));
if (valuesMethod != null) {
// 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(new ProgramMethod(clazz, valuesMethod), reason);
- keepInfo.keepMethod(clazz, valuesMethod);
- shouldNotBeMinified(valuesMethod.toReference());
+ workList.enqueueMarkMethodKeptAction(valuesMethod, reason);
+ keepInfo.keepMethod(valuesMethod);
+ shouldNotBeMinified(valuesMethod.getReference());
}
}
@@ -2734,25 +2735,26 @@
return appInfoWithLiveness;
}
- public NestedGraphLens buildGraphLens(AppView<?> appView) {
+ public NestedGraphLens buildGraphLens() {
return lambdaRewriter != null ? lambdaRewriter.fixup() : null;
}
private void keepClassWithRules(DexProgramClass clazz, Set<ProguardKeepRuleBase> rules) {
- keepInfo.joinClass(clazz, info -> applyKeepRules(rules, info));
+ keepInfo.joinClass(clazz, info -> applyKeepRules(clazz, rules, info));
}
- private void keepMethodWithRules(
- DexProgramClass holder, DexEncodedMethod method, Set<ProguardKeepRuleBase> rules) {
- keepInfo.joinMethod(holder, method, info -> applyKeepRules(rules, info));
+ private void keepMethodWithRules(ProgramMethod method, Set<ProguardKeepRuleBase> rules) {
+ keepInfo.joinMethod(method, info -> applyKeepRules(method, rules, info));
}
- private void keepFieldWithRules(
- DexProgramClass holder, DexEncodedField field, Set<ProguardKeepRuleBase> rules) {
- keepInfo.joinField(holder, field, info -> applyKeepRules(rules, info));
+ private void keepFieldWithRules(ProgramField field, Set<ProguardKeepRuleBase> rules) {
+ keepInfo.joinField(field, info -> applyKeepRules(field, rules, info));
}
- private void applyKeepRules(Set<ProguardKeepRuleBase> rules, KeepInfo.Joiner<?, ?, ?> joiner) {
+ private void applyKeepRules(
+ ProgramDefinition definition,
+ Set<ProguardKeepRuleBase> rules,
+ KeepInfo.Joiner<?, ?, ?> joiner) {
for (ProguardKeepRuleBase rule : rules) {
ProguardKeepRuleModifiers modifiers =
(rule.isProguardIfRule() ? rule.asProguardIfRule().getSubsequentRule() : rule)
@@ -2760,6 +2762,9 @@
if (!modifiers.allowsShrinking) {
// TODO(b/159589281): Evaluate this interpretation.
joiner.pin();
+ if (!definition.getAccessFlags().isPublic()) {
+ joiner.requireAccessModificationForRepackaging();
+ }
}
if (!modifiers.allowsObfuscation) {
joiner.disallowMinification();
@@ -3676,8 +3681,9 @@
clazz, null, InstantiationReason.REFLECTION, KeepReason.reflectiveUseIn(method));
}
if (!keepInfo.getFieldInfo(encodedField, clazz).isPinned()) {
- keepInfo.pinField(clazz, encodedField);
- markFieldAsKept(new ProgramField(clazz, encodedField), KeepReason.reflectiveUseIn(method));
+ ProgramField programField = new ProgramField(clazz, encodedField);
+ keepInfo.pinField(programField);
+ markFieldAsKept(programField, KeepReason.reflectiveUseIn(method));
}
} else {
assert identifierItem.isDexMethod();
@@ -3869,10 +3875,11 @@
// Also pin all of its virtual methods to ensure that the devirtualizer does not perform
// illegal rewritings of invoke-interface instructions into invoke-virtual instructions.
- for (DexEncodedMethod virtualMethod : clazz.virtualMethods()) {
- keepInfo.pinMethod(clazz, virtualMethod);
- markVirtualMethodAsReachable(virtualMethod.method, true, null, reason);
- }
+ clazz.forEachProgramVirtualMethod(
+ virtualMethod -> {
+ keepInfo.pinMethod(virtualMethod);
+ markVirtualMethodAsReachable(virtualMethod.getReference(), true, null, reason);
+ });
}
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
index 1e32804..40b2c9e 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
@@ -24,7 +24,8 @@
super(builder);
}
- private Builder builder() {
+ @Override
+ Builder builder() {
return new Builder(this);
}
@@ -33,18 +34,11 @@
return new Joiner(this);
}
- /**
- * True if a class may be repackaged.
- *
- * <p>This method requires knowledge of the global configuration as that can override the concrete
- * value on a given item.
- */
+ @Override
public boolean isRepackagingAllowed(GlobalKeepInfoConfiguration configuration) {
- return configuration.isRepackagingEnabled() && internalIsRepackagingAllowed();
- }
-
- boolean internalIsRepackagingAllowed() {
- return internalIsMinificationAllowed();
+ return configuration.isRepackagingEnabled()
+ && internalIsMinificationAllowed()
+ && !internalIsAccessModificationRequiredForRepackaging();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java
index 6f22db4..bdfb7cc 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.shaking;
/** Immutable keep requirements for a field. */
-public final class KeepFieldInfo extends KeepInfo<KeepFieldInfo.Builder, KeepFieldInfo> {
+public final class KeepFieldInfo extends KeepMemberInfo<KeepFieldInfo.Builder, KeepFieldInfo> {
// Requires all aspects of a field to be kept.
private static final KeepFieldInfo TOP = new Builder().makeTop().build();
@@ -26,6 +26,7 @@
// This builder is not private as there are known instances where it is safe to modify keep info
// in a non-upwards direction.
+ @Override
Builder builder() {
return new Builder(this);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
index a1f8424..4cb677f 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
@@ -6,23 +6,34 @@
import com.android.tools.r8.shaking.KeepInfo.Builder;
/** Keep information that can be associated with any item, i.e., class, method or field. */
-public abstract class KeepInfo<B extends Builder, K extends KeepInfo> {
+public abstract class KeepInfo<B extends Builder<B, K>, K extends KeepInfo<B, K>> {
private final boolean pinned;
private final boolean allowMinification;
private final boolean allowAccessModification;
+ private final boolean requireAccessModificationForRepackaging;
- private KeepInfo(boolean pinned, boolean allowMinification, boolean allowAccessModification) {
+ private KeepInfo(
+ boolean pinned,
+ boolean allowMinification,
+ boolean allowAccessModification,
+ boolean requireAccessModificationForRepackaging) {
this.pinned = pinned;
this.allowMinification = allowMinification;
this.allowAccessModification = allowAccessModification;
+ this.requireAccessModificationForRepackaging = requireAccessModificationForRepackaging;
}
KeepInfo(B builder) {
this(
- builder.isPinned(), builder.isMinificationAllowed(), builder.isAccessModificationAllowed());
+ builder.isPinned(),
+ builder.isMinificationAllowed(),
+ builder.isAccessModificationAllowed(),
+ builder.isAccessModificationRequiredForRepackaging());
}
+ abstract B builder();
+
/** True if an item must be present in the output. */
public boolean isPinned() {
return pinned;
@@ -43,6 +54,18 @@
}
/**
+ * True if an item may be repackaged.
+ *
+ * <p>This method requires knowledge of the global configuration as that can override the concrete
+ * value on a given item.
+ */
+ public abstract boolean isRepackagingAllowed(GlobalKeepInfoConfiguration configuration);
+
+ boolean internalIsAccessModificationRequiredForRepackaging() {
+ return requireAccessModificationForRepackaging;
+ }
+
+ /**
* True if an item may have its access flags modified.
*
* <p>This method requires knowledge of the global access modification as that will override the
@@ -71,7 +94,7 @@
}
/** Builder to construct an arbitrary keep info object. */
- public abstract static class Builder<B extends Builder, K extends KeepInfo> {
+ public abstract static class Builder<B extends Builder<B, K>, K extends KeepInfo<B, K>> {
abstract B self();
@@ -87,6 +110,7 @@
private boolean pinned;
private boolean allowMinification;
private boolean allowAccessModification;
+ private boolean requireAccessModificationForRepackaging;
Builder() {
// Default initialized. Use should be followed by makeTop/makeBottom.
@@ -97,11 +121,14 @@
pinned = original.isPinned();
allowMinification = original.internalIsMinificationAllowed();
allowAccessModification = original.internalIsAccessModificationAllowed();
+ requireAccessModificationForRepackaging =
+ original.internalIsAccessModificationRequiredForRepackaging();
}
B makeTop() {
pin();
disallowMinification();
+ requireAccessModificationForRepackaging();
disallowAccessModification();
return self();
}
@@ -109,6 +136,7 @@
B makeBottom() {
unpin();
allowMinification();
+ unsetRequireAccessModificationForRepackaging();
allowAccessModification();
return self();
}
@@ -131,6 +159,8 @@
private boolean internalIsEqualTo(K other) {
return isPinned() == other.isPinned()
&& isMinificationAllowed() == other.internalIsMinificationAllowed()
+ && isAccessModificationRequiredForRepackaging()
+ == other.internalIsAccessModificationRequiredForRepackaging()
&& isAccessModificationAllowed() == other.internalIsAccessModificationAllowed()
&& isEqualTo(other);
}
@@ -143,6 +173,10 @@
return allowMinification;
}
+ public boolean isAccessModificationRequiredForRepackaging() {
+ return requireAccessModificationForRepackaging;
+ }
+
public boolean isAccessModificationAllowed() {
return allowAccessModification;
}
@@ -173,6 +207,20 @@
return setAllowMinification(false);
}
+ public B setRequireAccessModificationForRepackaging(
+ boolean requireAccessModificationForRepackaging) {
+ this.requireAccessModificationForRepackaging = requireAccessModificationForRepackaging;
+ return self();
+ }
+
+ public B requireAccessModificationForRepackaging() {
+ return setRequireAccessModificationForRepackaging(true);
+ }
+
+ public B unsetRequireAccessModificationForRepackaging() {
+ return setRequireAccessModificationForRepackaging(false);
+ }
+
public B setAllowAccessModification(boolean allowAccessModification) {
this.allowAccessModification = allowAccessModification;
return self();
@@ -189,7 +237,7 @@
/** Joiner to construct monotonically increasing keep info object. */
public abstract static class Joiner<
- J extends Joiner, B extends Builder, K extends KeepInfo<B, K>> {
+ J extends Joiner<J, B, K>, B extends Builder<B, K>, K extends KeepInfo<B, K>> {
abstract J self();
@@ -223,6 +271,11 @@
return self();
}
+ public J requireAccessModificationForRepackaging() {
+ builder.requireAccessModificationForRepackaging();
+ return self();
+ }
+
public K join() {
K joined = builder.build();
K original = builder.original;
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
index d2cbf66..0881bef 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
@@ -17,6 +16,7 @@
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
+import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.shaking.KeepFieldInfo.Joiner;
@@ -27,6 +27,7 @@
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.function.Consumer;
// Non-mutable collection of keep information pertaining to a program.
@@ -104,7 +105,7 @@
return definition == null ? KeepFieldInfo.bottom() : getFieldInfo(definition, holder);
}
- public final KeepInfo getInfo(DexReference reference, DexDefinitionSupplier definitions) {
+ public final KeepInfo<?, ?> getInfo(DexReference reference, DexDefinitionSupplier definitions) {
if (reference.isDexType()) {
return getClassInfo(reference.asDexType(), definitions);
}
@@ -117,6 +118,19 @@
throw new Unreachable();
}
+ public final KeepInfo<?, ?> getInfo(ProgramDefinition definition) {
+ if (definition.isProgramClass()) {
+ return getClassInfo(definition.asProgramClass());
+ }
+ if (definition.isProgramMethod()) {
+ return getMethodInfo(definition.asProgramMethod());
+ }
+ if (definition.isProgramField()) {
+ return getFieldInfo(definition.asProgramField());
+ }
+ throw new Unreachable();
+ }
+
public final boolean isPinned(DexReference reference, DexDefinitionSupplier definitions) {
return getInfo(reference, definitions).isPinned();
}
@@ -197,6 +211,12 @@
this.ruleInstances = ruleInstances;
}
+ public void removeKeepInfoForPrunedItems(Set<DexType> removedClasses) {
+ keepClassInfo.keySet().removeIf(removedClasses::contains);
+ keepFieldInfo.keySet().removeIf(field -> removedClasses.contains(field.getHolderType()));
+ keepMethodInfo.keySet().removeIf(method -> removedClasses.contains(method.getHolderType()));
+ }
+
@Override
public KeepInfoCollection rewrite(NonIdentityGraphLens lens, InternalOptions options) {
Map<DexType, KeepClassInfo> newClassInfo = new IdentityHashMap<>(keepClassInfo.size());
@@ -204,7 +224,8 @@
(type, info) -> {
DexType newType = lens.lookupType(type);
assert newType == type || !info.isPinned() || info.isMinificationAllowed(options);
- newClassInfo.put(newType, info);
+ KeepClassInfo previous = newClassInfo.put(newType, info);
+ assert previous == null;
});
Map<DexMethod, KeepMethodInfo> newMethodInfo = new IdentityHashMap<>(keepMethodInfo.size());
keepMethodInfo.forEach(
@@ -222,7 +243,9 @@
.allMatch(x -> x);
assert !info.isPinned()
|| newMethod.getReturnType() == lens.lookupType(method.getReturnType());
- newMethodInfo.put(newMethod, info);
+ KeepMethodInfo previous = newMethodInfo.put(newMethod, info);
+ // TODO(b/169927809): Avoid collisions.
+ // assert previous == null;
});
Map<DexField, KeepFieldInfo> newFieldInfo = new IdentityHashMap<>(keepFieldInfo.size());
keepFieldInfo.forEach(
@@ -231,7 +254,8 @@
assert newField.name == field.name
|| !info.isPinned()
|| info.isMinificationAllowed(options);
- newFieldInfo.put(newField, info);
+ KeepFieldInfo previous = newFieldInfo.put(newField, info);
+ assert previous == null;
});
Map<DexReference, List<Consumer<KeepInfo.Joiner<?, ?, ?>>>> newRuleInstances =
new IdentityHashMap<>(ruleInstances.size());
@@ -315,17 +339,17 @@
} else if (reference.isDexMethod()) {
DexMethod method = reference.asDexMethod();
DexProgramClass clazz = asProgramClassOrNull(definitions.definitionFor(method.holder));
- DexEncodedMethod definition = method.lookupOnClass(clazz);
+ ProgramMethod definition = method.lookupOnProgramClass(clazz);
if (definition != null) {
- joinMethod(clazz, definition, fn::accept);
+ joinMethod(definition, fn::accept);
}
} else {
assert reference.isDexField();
DexField field = reference.asDexField();
DexProgramClass clazz = asProgramClassOrNull(definitions.definitionFor(field.holder));
- DexEncodedField definition = field.lookupOnClass(clazz);
+ ProgramField definition = field.lookupOnProgramClass(clazz);
if (definition != null) {
- joinField(clazz, definition, fn::accept);
+ joinField(definition, fn::accept);
}
}
}
@@ -338,9 +362,8 @@
joinClass(clazz, KeepInfo.Joiner::pin);
}
- public void joinMethod(
- DexProgramClass holder, DexEncodedMethod method, Consumer<KeepMethodInfo.Joiner> fn) {
- KeepMethodInfo info = getMethodInfo(method, holder);
+ public void joinMethod(ProgramMethod method, Consumer<KeepMethodInfo.Joiner> fn) {
+ KeepMethodInfo info = getMethodInfo(method);
if (info == KeepMethodInfo.top()) {
return;
}
@@ -348,24 +371,16 @@
fn.accept(joiner);
KeepMethodInfo joined = joiner.join();
if (!info.equals(joined)) {
- keepMethodInfo.put(method.method, joined);
+ keepMethodInfo.put(method.getReference(), joined);
}
}
- public void joinMethod(ProgramMethod programMethod, Consumer<KeepMethodInfo.Joiner> fn) {
- joinMethod(programMethod.getHolder(), programMethod.getDefinition(), fn);
+ public void keepMethod(ProgramMethod method) {
+ joinMethod(method, KeepInfo.Joiner::top);
}
- public void keepMethod(ProgramMethod programMethod) {
- keepMethod(programMethod.getHolder(), programMethod.getDefinition());
- }
-
- public void keepMethod(DexProgramClass holder, DexEncodedMethod method) {
- joinMethod(holder, method, KeepInfo.Joiner::top);
- }
-
- public void pinMethod(DexProgramClass holder, DexEncodedMethod method) {
- joinMethod(holder, method, KeepInfo.Joiner::pin);
+ public void pinMethod(ProgramMethod method) {
+ joinMethod(method, KeepInfo.Joiner::pin);
}
public void unsafeAllowMinificationOfMethod(ProgramMethod method) {
@@ -396,9 +411,31 @@
}
}
- public void joinField(
- DexProgramClass holder, DexEncodedField field, Consumer<KeepFieldInfo.Joiner> fn) {
- KeepFieldInfo info = getFieldInfo(field, holder);
+ public void unsetRequireAllowAccessModificationForRepackaging(ProgramDefinition definition) {
+ if (definition.isProgramClass()) {
+ DexProgramClass clazz = definition.asProgramClass();
+ KeepClassInfo info = getClassInfo(clazz);
+ keepClassInfo.put(
+ clazz.getType(), info.builder().unsetRequireAccessModificationForRepackaging().build());
+ } else if (definition.isProgramMethod()) {
+ ProgramMethod method = definition.asProgramMethod();
+ KeepMethodInfo info = getMethodInfo(method);
+ keepMethodInfo.put(
+ method.getReference(),
+ info.builder().unsetRequireAccessModificationForRepackaging().build());
+ } else if (definition.isProgramField()) {
+ ProgramField field = definition.asProgramField();
+ KeepFieldInfo info = getFieldInfo(field);
+ keepFieldInfo.put(
+ field.getReference(),
+ info.builder().unsetRequireAccessModificationForRepackaging().build());
+ } else {
+ throw new Unreachable();
+ }
+ }
+
+ public void joinField(ProgramField field, Consumer<KeepFieldInfo.Joiner> fn) {
+ KeepFieldInfo info = getFieldInfo(field);
if (info.isTop()) {
return;
}
@@ -406,29 +443,16 @@
fn.accept(joiner);
KeepFieldInfo joined = joiner.join();
if (!info.equals(joined)) {
- keepFieldInfo.put(field.field, joined);
+ keepFieldInfo.put(field.getReference(), joined);
}
}
- public void keepField(ProgramField programField) {
- keepField(programField.getHolder(), programField.getDefinition());
+ public void keepField(ProgramField field) {
+ joinField(field, KeepInfo.Joiner::top);
}
- public void keepField(DexProgramClass holder, DexEncodedField field) {
- joinField(holder, field, KeepInfo.Joiner::top);
- }
-
- public void pinField(DexProgramClass holder, DexEncodedField field) {
- joinField(holder, field, KeepInfo.Joiner::pin);
- }
-
- public void keepMember(DexProgramClass holder, DexEncodedMember<?, ?> member) {
- if (member.isDexEncodedMethod()) {
- keepMethod(holder, member.asDexEncodedMethod());
- } else {
- assert member.isDexEncodedField();
- keepField(holder, member.asDexEncodedField());
- }
+ public void pinField(ProgramField field) {
+ joinField(field, KeepInfo.Joiner::pin);
}
public void unsafeAllowMinificationOfField(ProgramField field) {
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java
new file mode 100644
index 0000000..770e84c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java
@@ -0,0 +1,21 @@
+// 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;
+
+import com.android.tools.r8.shaking.KeepInfo.Builder;
+
+/** Immutable keep requirements for a member. */
+public abstract class KeepMemberInfo<B extends Builder<B, K>, K extends KeepInfo<B, K>>
+ extends KeepInfo<B, K> {
+
+ KeepMemberInfo(B builder) {
+ super(builder);
+ }
+
+ @Override
+ public boolean isRepackagingAllowed(GlobalKeepInfoConfiguration configuration) {
+ return configuration.isRepackagingEnabled()
+ && !internalIsAccessModificationRequiredForRepackaging();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
index 98eae1b..ab243b6 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.shaking;
/** Immutable keep requirements for a method. */
-public final class KeepMethodInfo extends KeepInfo<KeepMethodInfo.Builder, KeepMethodInfo> {
+public final class KeepMethodInfo extends KeepMemberInfo<KeepMethodInfo.Builder, KeepMethodInfo> {
// Requires all aspects of a method to be kept.
private static final KeepMethodInfo TOP = new Builder().makeTop().build();
@@ -26,6 +26,7 @@
// This builder is not private as there are known instances where it is safe to modify keep info
// in a non-upwards direction.
+ @Override
Builder builder() {
return new Builder(this);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 951e2e5..18becf3 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -36,6 +36,7 @@
private final AppView<AppInfoWithLiveness> appView;
private final TreePrunerConfiguration configuration;
private final UnusedItemsPrinter unusedItemsPrinter;
+ private final Set<DexType> missingTypes;
private final Set<DexType> prunedTypes = Sets.newIdentityHashSet();
private final Set<DexMethod> methodsToKeepForConfigurationDebugging = Sets.newIdentityHashSet();
@@ -47,6 +48,7 @@
InternalOptions options = appView.options();
this.appView = appView;
this.configuration = configuration;
+ this.missingTypes = appView.appInfo().getMissingTypes();
this.unusedItemsPrinter =
options.hasUsageInformationConsumer()
? new UnusedItemsPrinter(
@@ -174,7 +176,7 @@
if (reachableStaticFields != null) {
clazz.setStaticFields(reachableStaticFields);
}
- clazz.removeInnerClasses(this::isAttributeReferencingPrunedType);
+ clazz.removeInnerClasses(this::isAttributeReferencingMissingOrPrunedType);
clazz.removeEnclosingMethodAttribute(this::isAttributeReferencingPrunedItem);
rewriteNestAttributes(clazz);
unusedItemsPrinter.visited();
@@ -195,6 +197,10 @@
}
}
+ private boolean isTypeMissing(DexType type) {
+ return missingTypes.contains(type);
+ }
+
private boolean isTypeLive(DexType type) {
return appView.appInfo().isNonProgramTypeOrLiveProgramType(type);
}
@@ -238,13 +244,12 @@
&& !appInfo.liveMethods.contains(attr.getEnclosingMethod()));
}
- private boolean isAttributeReferencingPrunedType(InnerClassAttribute attr) {
- AppInfoWithLiveness appInfo = appView.appInfo();
- if (!isTypeLive(attr.getInner())) {
+ private boolean isAttributeReferencingMissingOrPrunedType(InnerClassAttribute attr) {
+ if (isTypeMissing(attr.getInner()) || !isTypeLive(attr.getInner())) {
return true;
}
- DexType context = attr.getLiveContext(appInfo);
- return context == null || !isTypeLive(context);
+ DexType context = attr.getLiveContext(appView);
+ return context == null || isTypeMissing(context) || !isTypeLive(context);
}
private <D extends DexEncodedMember<D, R>, R extends DexMember<D, R>> int firstUnreachableIndex(
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 0815577..3f8077f 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -639,10 +639,19 @@
Log.debug(getClass(), "Merged %d classes.", mergedClasses.size());
}
timing.end();
+
+ if (mergedClasses.isEmpty()) {
+ return null;
+ }
+
timing.begin("fixup");
VerticalClassMergerGraphLens lens = new TreeFixer().fixupTypeReferences();
+ KeepInfoCollection keepInfo = appView.appInfo().getKeepInfo();
+ keepInfo.mutate(mutator -> mutator.removeKeepInfoForPrunedItems(mergedClasses.keySet()));
timing.end();
- assert lens == null || verifyGraphLens(lens);
+
+ assert lens != null;
+ assert verifyGraphLens(lens);
return lens;
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
index 1091d09..80c47f2 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.EnclosingMethodAttribute;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.NestHostClassAttribute;
import com.android.tools.r8.graph.NestMemberClassAttribute;
@@ -71,7 +72,6 @@
List<NestMemberClassAttribute> nestMembers = Collections.emptyList();
EnclosingMethodAttribute enclosingMembers = null;
List<InnerClassAttribute> innerClasses = Collections.emptyList();
- DexAnnotationSet classAnnotations = DexAnnotationSet.empty();
DexEncodedField[] staticFields = DexEncodedField.EMPTY_ARRAY;
DexEncodedField[] instanceFields = DexEncodedField.EMPTY_ARRAY;
DexEncodedMethod[] directMethods = DexEncodedMethod.EMPTY_ARRAY;
@@ -106,7 +106,8 @@
nestMembers,
enclosingMembers,
innerClasses,
- classAnnotations,
+ ClassSignature.NO_CLASS_SIGNATURE,
+ DexAnnotationSet.empty(),
staticFields,
instanceFields,
directMethods,
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Formatter.java b/src/main/java/com/android/tools/r8/tracereferences/Formatter.java
new file mode 100644
index 0000000..b545e65
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/tracereferences/Formatter.java
@@ -0,0 +1,176 @@
+// 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.tracereferences;
+
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.PackageReference;
+import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedClass;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedField;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedMethod;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+abstract class Formatter {
+
+ private final StringBuilder output;
+
+ Formatter() {
+ output = new StringBuilder();
+ }
+
+ String get() {
+ return output.toString();
+ }
+
+ protected void append(String string) {
+ output.append(string);
+ }
+
+ protected void appendLine(String string) {
+ output.append(StringUtils.lines(string));
+ }
+
+ protected void appendLine() {
+ appendLine("");
+ }
+
+ protected void printArguments(MethodReference method) {
+ StringUtils.append(
+ output,
+ ListUtils.map(method.getFormalTypes(), TypeReference::getTypeName),
+ ",",
+ BraceType.PARENS);
+ }
+
+ protected abstract void printConstructorName(MethodReference method);
+
+ private void printError(String message) {
+ append("# Error: " + message);
+ }
+
+ protected abstract void printField(TracedField field);
+
+ protected abstract void printMethod(TracedMethod method);
+
+ private void printFieldError(FieldReference field) {
+ appendLine(
+ field.getFieldType().getTypeName()
+ + " "
+ + field.getHolderClass().getTypeName()
+ + "."
+ + field.getFieldName());
+ }
+
+ private void printMethodError(MethodReference method) {
+ printReturn(method);
+ append(" ");
+ append(method.getHolderClass().getTypeName());
+ append(".");
+ append(method.getMethodName());
+ printArguments(method);
+ appendLine();
+ }
+
+ protected abstract void printPackageNames(List<String> packageNames);
+
+ protected void printReturn(MethodReference method) {
+ append(method.getReturnType() != null ? method.getReturnType().getTypeName() : "void");
+ }
+
+ protected void printNameAndReturn(MethodReference method) {
+ if (method.getMethodName().equals("<init>")) {
+ printConstructorName(method);
+ } else {
+ printReturn(method);
+ append(" ");
+ append(method.getMethodName());
+ }
+ }
+
+ protected abstract void printTypeHeader(TracedClass clazz);
+
+ protected abstract void printTypeFooter();
+
+ void format(TraceReferencesResult result) {
+ int errors =
+ print(
+ result.types,
+ result.keepPackageNames,
+ result.fields,
+ result.methods,
+ result.missingDefinition);
+ assert errors == result.missingDefinition.size();
+ }
+
+ private int print(
+ Set<TracedClass> types,
+ Set<PackageReference> keepPackageNames,
+ Map<ClassReference, Set<TracedField>> fields,
+ Map<ClassReference, Set<TracedMethod>> methods,
+ Set<Object> missingDefinition) {
+ int errors = 0;
+ List<TracedClass> sortedTypes = new ArrayList<>(types);
+ sortedTypes.sort(Comparator.comparing(tracedClass -> tracedClass.getReference().getTypeName()));
+ for (TracedClass type : sortedTypes) {
+ if (missingDefinition.contains(type)) {
+ printError("Could not find definition for type " + type.getReference().getTypeName());
+ errors++;
+ continue;
+ }
+ printTypeHeader(type);
+ Set<TracedMethod> methodsForClass = methods.get(type.getReference());
+ if (methodsForClass != null) {
+ List<TracedMethod> sortedMethods = new ArrayList<>(methods.get(type.getReference()).size());
+ for (TracedMethod method : methods.get(type.getReference())) {
+ if (method.isMissingDefinition()) {
+ printError("Could not find definition for method ");
+ printMethodError(method.getReference());
+ errors++;
+ continue;
+ }
+ assert method.getAccessFlags() != null;
+ sortedMethods.add(method);
+ }
+ sortedMethods.sort(
+ Comparator.comparing(tracedMethod -> tracedMethod.getReference().toString()));
+ for (TracedMethod method : sortedMethods) {
+ printMethod(method);
+ }
+ }
+ Set<TracedField> fieldsForClass = fields.get(type.getReference());
+ if (fieldsForClass != null) {
+ List<TracedField> sortedFields = new ArrayList<>(fieldsForClass);
+ sortedFields.sort(
+ Comparator.comparing(tracedField -> tracedField.getReference().toString()));
+ for (TracedField field : sortedFields) {
+ if (field.isMissingDefinition()) {
+ printError("Could not find definition for field ");
+ printFieldError(field.getReference());
+ errors++;
+ continue;
+ }
+ printField(field);
+ }
+ }
+ printTypeFooter();
+ }
+ List<String> packageNamesToKeep =
+ keepPackageNames.stream()
+ .map(PackageReference::getPackageName)
+ .sorted()
+ .collect(Collectors.toList());
+ printPackageNames(packageNamesToKeep);
+ return errors;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/KeepRuleFormatter.java b/src/main/java/com/android/tools/r8/tracereferences/KeepRuleFormatter.java
index f259b01..85352f2 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/KeepRuleFormatter.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/KeepRuleFormatter.java
@@ -3,70 +3,66 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.tracereferences;
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.StringConsumer;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedClass;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedField;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedMethod;
import com.android.tools.r8.utils.StringUtils;
import java.util.List;
-class KeepRuleFormatter extends ResultFormatter {
+class KeepRuleFormatter extends Formatter {
final boolean allowObfuscation;
- KeepRuleFormatter(
- StringConsumer output, DiagnosticsHandler diagnosticsHandler, boolean allowObfuscation) {
- super(output, diagnosticsHandler);
+ KeepRuleFormatter(boolean allowObfuscation) {
this.allowObfuscation = allowObfuscation;
}
@Override
- protected void printTypeHeader(DexClass dexClass) {
+ protected void printTypeHeader(TracedClass tracedClass) {
append(allowObfuscation ? "-keep,allowobfuscation" : "-keep");
- if (dexClass.isInterface()) {
- append(" interface " + dexClass.type.toSourceString() + " {" + System.lineSeparator());
- } else if (dexClass.accessFlags.isEnum()) {
- append(" enum " + dexClass.type.toSourceString() + " {" + System.lineSeparator());
+ if (tracedClass.getAccessFlags().isInterface()) {
+ appendLine(" interface " + tracedClass.getReference().getTypeName() + " {");
+ } else if (tracedClass.getAccessFlags().isEnum()) {
+ appendLine(" enum " + tracedClass.getReference().getTypeName() + " {");
} else {
- append(" class " + dexClass.type.toSourceString() + " {" + System.lineSeparator());
+ appendLine(" class " + tracedClass.getReference().getTypeName() + " {");
}
}
@Override
- protected void printConstructorName(DexEncodedMethod encodedMethod) {
+ protected void printConstructorName(MethodReference method) {
append("<init>");
}
@Override
- protected void printField(DexClass dexClass, DexField field) {
+ protected void printField(TracedField field) {
append(
" "
- + field.type.toSourceString()
+ + field.getReference().getFieldType().getTypeName()
+ " "
- + field.name.toString()
+ + field.getReference().getFieldName()
+ ";"
+ System.lineSeparator());
}
@Override
- protected void printMethod(DexEncodedMethod encodedMethod, String typeName) {
- // Static initializers do not require keep rules - it is kept by keeping the class.
- if (encodedMethod.accessFlags.isConstructor() && encodedMethod.accessFlags.isStatic()) {
+ protected void printMethod(TracedMethod tracedMethod) {
+ if (tracedMethod.getReference().getMethodName().equals("<clinit>")) {
return;
}
append(" ");
- if (encodedMethod.isPublicMethod()) {
+ if (tracedMethod.getAccessFlags().isPublic()) {
append("public ");
- } else if (encodedMethod.isPrivateMethod()) {
+ } else if (tracedMethod.getAccessFlags().isPrivate()) {
append("private ");
- } else if (encodedMethod.isProtectedMethod()) {
+ } else if (tracedMethod.getAccessFlags().isProtected()) {
append("protected ");
}
- if (encodedMethod.isStatic()) {
+ if (tracedMethod.getAccessFlags().isStatic()) {
append("static ");
}
- printNameAndReturn(encodedMethod);
- printArguments(encodedMethod.method);
+ printNameAndReturn(tracedMethod.getReference());
+ printArguments(tracedMethod.getReference());
appendLine(";");
}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/PrintUsesFormatter.java b/src/main/java/com/android/tools/r8/tracereferences/PrintUsesFormatter.java
index 12b2e6b..a29fdb5 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/PrintUsesFormatter.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/PrintUsesFormatter.java
@@ -3,35 +3,30 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.tracereferences;
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.StringConsumer;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedClass;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedField;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedMethod;
import java.util.List;
-class PrintUsesFormatter extends ResultFormatter {
-
- PrintUsesFormatter(StringConsumer output, DiagnosticsHandler diagnosticsHandler) {
- super(output, diagnosticsHandler);
- }
+class PrintUsesFormatter extends Formatter {
@Override
- protected void printConstructorName(DexEncodedMethod encodedMethod) {
- if (encodedMethod.accessFlags.isStatic()) {
+ protected void printConstructorName(MethodReference method) {
+ if (method.getMethodName().equals("<clinit>")) {
append("<clinit>");
} else {
- String holderName = encodedMethod.holder().toSourceString();
+ String holderName = method.getHolderClass().getTypeName();
String constructorName = holderName.substring(holderName.lastIndexOf('.') + 1);
append(constructorName);
}
}
@Override
- protected void printMethod(DexEncodedMethod encodedMethod, String typeName) {
- append(typeName + ": ");
- printNameAndReturn(encodedMethod);
- printArguments(encodedMethod.method);
+ protected void printMethod(TracedMethod method) {
+ append(method.getReference().getHolderClass().getTypeName() + ": ");
+ printNameAndReturn(method.getReference());
+ printArguments(method.getReference());
appendLine("");
}
@@ -41,20 +36,20 @@
}
@Override
- protected void printTypeHeader(DexClass dexClass) {
- appendLine(dexClass.type.toSourceString());
+ protected void printTypeHeader(TracedClass type) {
+ appendLine(type.getReference().getTypeName());
}
@Override
protected void printTypeFooter() {}
@Override
- protected void printField(DexClass dexClass, DexField field) {
+ protected void printField(TracedField field) {
appendLine(
- dexClass.type.toSourceString()
+ field.getReference().getHolderClass().getTypeName()
+ ": "
- + field.type.toSourceString()
+ + field.getReference().getFieldType().getTypeName()
+ " "
- + field.name.toString());
+ + field.getReference().getFieldName());
}
}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Result.java b/src/main/java/com/android/tools/r8/tracereferences/Result.java
deleted file mode 100644
index 6889f0c..0000000
--- a/src/main/java/com/android/tools/r8/tracereferences/Result.java
+++ /dev/null
@@ -1,36 +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.tracereferences;
-
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexReference;
-import com.android.tools.r8.graph.DexType;
-import java.util.Map;
-import java.util.Set;
-
-class Result {
- final DexApplication application;
- final Set<DexType> types;
- final Set<String> keepPackageNames;
- final Map<DexType, Set<DexField>> fields;
- final Map<DexType, Set<DexMethod>> methods;
- final Set<DexReference> missingDefinition;
-
- Result(
- DexApplication application,
- Set<DexType> types,
- Set<String> keepPackageNames,
- Map<DexType, Set<DexField>> fields,
- Map<DexType, Set<DexMethod>> methods,
- Set<DexReference> missingDefinition) {
- this.application = application;
- this.types = types;
- this.keepPackageNames = keepPackageNames;
- this.fields = fields;
- this.methods = methods;
- this.missingDefinition = missingDefinition;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/ResultFormatter.java b/src/main/java/com/android/tools/r8/tracereferences/ResultFormatter.java
deleted file mode 100644
index 2fd8934..0000000
--- a/src/main/java/com/android/tools/r8/tracereferences/ResultFormatter.java
+++ /dev/null
@@ -1,142 +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.tracereferences;
-
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.StringConsumer;
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexReference;
-import com.android.tools.r8.graph.DexType;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-abstract class ResultFormatter {
-
- private final StringConsumer output;
- private final DiagnosticsHandler diagnosticsHandler;
-
- ResultFormatter(StringConsumer output, DiagnosticsHandler diagnosticsHandler) {
- this.output = output;
- this.diagnosticsHandler = diagnosticsHandler;
- }
-
- protected void append(String string) {
- output.accept(string, diagnosticsHandler);
- }
-
- protected void appendLine(String string) {
- output.accept(string + System.lineSeparator(), diagnosticsHandler);
- }
-
- protected void printArguments(DexMethod method) {
- append("(");
- for (int i = 0; i < method.getArity(); i++) {
- if (i != 0) {
- append(",");
- }
- append(method.proto.parameters.values[i].toSourceString());
- }
- append(")");
- }
-
- protected abstract void printConstructorName(DexEncodedMethod encodedMethod);
-
- private void printError(String message) {
- appendLine("# Error: " + message);
- }
-
- protected abstract void printField(DexClass dexClass, DexField field);
-
- protected abstract void printMethod(DexEncodedMethod encodedMethod, String typeName);
-
- protected abstract void printPackageNames(List<String> packageNames);
-
- protected void printNameAndReturn(DexEncodedMethod encodedMethod) {
- if (encodedMethod.accessFlags.isConstructor()) {
- printConstructorName(encodedMethod);
- } else {
- DexMethod method = encodedMethod.method;
- append(method.proto.returnType.toSourceString());
- append(" ");
- append(method.name.toSourceString());
- }
- }
-
- protected abstract void printTypeHeader(DexClass dexClass);
-
- protected abstract void printTypeFooter();
-
- void format(Result result) {
- int errors =
- print(
- result.application,
- result.types,
- result.keepPackageNames,
- result.fields,
- result.methods,
- result.missingDefinition);
- output.finished(diagnosticsHandler);
- assert errors == result.missingDefinition.size();
- }
-
- private int print(
- DexApplication application,
- Set<DexType> types,
- Set<String> keepPackageNames,
- Map<DexType, Set<DexField>> fields,
- Map<DexType, Set<DexMethod>> methods,
- Set<DexReference> missingDefinition) {
- int errors = 0;
- List<DexType> sortedTypes = new ArrayList<>(types);
- sortedTypes.sort(Comparator.comparing(DexType::toSourceString));
- for (DexType type : sortedTypes) {
- DexClass dexClass = application.definitionFor(type);
- if (missingDefinition.contains(type)) {
- assert dexClass == null;
- printError("Could not find definition for type " + type.toSourceString());
- errors++;
- continue;
- }
- printTypeHeader(dexClass);
- List<DexEncodedMethod> methodDefinitions = new ArrayList<>(methods.size());
- for (DexMethod method : methods.get(type)) {
- DexEncodedMethod encodedMethod = dexClass.lookupMethod(method);
- if (missingDefinition.contains(method)) {
- assert encodedMethod == null;
- printError("Could not find definition for method " + method.toSourceString());
- errors++;
- continue;
- }
- methodDefinitions.add(encodedMethod);
- }
- methodDefinitions.sort(Comparator.comparing(x -> x.method.name.toSourceString()));
- for (DexEncodedMethod encodedMethod : methodDefinitions) {
- printMethod(encodedMethod, dexClass.type.toSourceString());
- }
- List<DexField> sortedFields = new ArrayList<>(fields.get(type));
- sortedFields.sort(Comparator.comparing(DexField::toSourceString));
- for (DexField field : sortedFields) {
- if (missingDefinition.contains(field)) {
- printError("Could not find definition for field " + field.toSourceString());
- errors++;
- continue;
- }
- printField(dexClass, field);
- }
- printTypeFooter();
- }
- ArrayList<String> packageNamesToKeep = new ArrayList<>(keepPackageNames);
- Collections.sort(packageNamesToKeep);
- printPackageNames(packageNamesToKeep);
- return errors;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
index d0ec93e..acc2cb6 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
@@ -32,7 +32,7 @@
throw new CompilationFailedException();
} catch (Exception e) {
command.getDiagnosticsHandler().error(new ExceptionDiagnostic(e));
- throw new CompilationFailedException();
+ throw new CompilationFailedException(e);
}
}
@@ -46,8 +46,8 @@
if (command.getSource().isEmpty()) {
throw new TraceReferencesException("No source specified");
}
- if (command.getOutput() == null) {
- throw new TraceReferencesException("No output specified");
+ if (command.getConsumer() == null) {
+ throw new TraceReferencesException("No consumer specified");
}
AndroidApp.Builder builder = AndroidApp.builder();
command.getLibrary().forEach(builder::addLibraryResourceProvider);
@@ -80,25 +80,8 @@
}
}
}
- Tracer tracer = new Tracer(tagetDescriptors, builder.build());
- Result result = tracer.run();
- ResultFormatter formatter;
- switch (command.getOutputFormat()) {
- case PRINTUSAGE:
- formatter = new PrintUsesFormatter(command.getOutput(), command.getDiagnosticsHandler());
- break;
- case KEEP_RULES:
- formatter =
- new KeepRuleFormatter(command.getOutput(), command.getDiagnosticsHandler(), false);
- break;
- case KEEP_RULES_WITH_ALLOWOBFUSCATION:
- formatter =
- new KeepRuleFormatter(command.getOutput(), command.getDiagnosticsHandler(), true);
- break;
- default:
- throw new TraceReferencesException("Unexpected format " + command.getOutputFormat().name());
- }
- formatter.format(result);
+ Tracer tracer = new Tracer(tagetDescriptors, builder.build(), command.getDiagnosticsHandler());
+ tracer.run(command.getConsumer());
}
public static void run(String... args) throws CompilationFailedException {
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java
index f6fcb69..ae0ee57 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java
@@ -11,10 +11,8 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.Keep;
import com.android.tools.r8.ProgramResourceProvider;
-import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
-import com.android.tools.r8.tracereferences.TraceReferencesCommandParser.OutputFormat;
import com.android.tools.r8.utils.ArchiveResourceProvider;
import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -35,8 +33,7 @@
private final ImmutableList<ClassFileResourceProvider> library;
private final ImmutableList<ClassFileResourceProvider> traceTarget;
private final ImmutableList<ProgramResourceProvider> traceSource;
- private final StringConsumer output;
- private final OutputFormat outputFormat;
+ private final TraceReferencesConsumer consumer;
TraceReferencesCommand(
boolean printHelp,
@@ -45,16 +42,14 @@
ImmutableList<ClassFileResourceProvider> library,
ImmutableList<ClassFileResourceProvider> traceTarget,
ImmutableList<ProgramResourceProvider> traceSource,
- StringConsumer output,
- OutputFormat outputFormat) {
+ TraceReferencesConsumer consumer) {
this.printHelp = printHelp;
this.printVersion = printVersion;
this.diagnosticsHandler = diagnosticsHandler;
this.library = library;
this.traceTarget = traceTarget;
this.traceSource = traceSource;
- this.output = output;
- this.outputFormat = outputFormat;
+ this.consumer = consumer;
}
/**
@@ -101,8 +96,7 @@
ImmutableList.builder();
private final ImmutableList.Builder<ProgramResourceProvider> traceSourceBuilder =
ImmutableList.builder();
- private StringConsumer output;
- private OutputFormat outputFormat = TraceReferencesCommandParser.OutputFormat.PRINTUSAGE;
+ private TraceReferencesConsumer consumer;
private Builder(DiagnosticsHandler diagnosticsHandler) {
this.diagnosticsHandler = diagnosticsHandler;
@@ -197,13 +191,8 @@
return this;
}
- Builder setOutputPath(Path output) {
- this.output = new StringConsumer.FileConsumer(output);
- return this;
- }
-
- Builder setOutputFormat(OutputFormat outputFormat) {
- this.outputFormat = outputFormat;
+ Builder setConsumer(TraceReferencesConsumer consumer) {
+ this.consumer = consumer;
return this;
}
@@ -217,8 +206,7 @@
libraryBuilder.build(),
traceTarget,
traceSource,
- output,
- outputFormat);
+ consumer);
}
void error(Diagnostic diagnostic) {
@@ -244,11 +232,7 @@
return traceSource;
}
- StringConsumer getOutput() {
- return output;
- }
-
- OutputFormat getOutputFormat() {
- return outputFormat;
+ TraceReferencesConsumer getConsumer() {
+ return consumer;
}
}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommandParser.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommandParser.java
index 79de8e2..476d0d4 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommandParser.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommandParser.java
@@ -6,11 +6,13 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.JdkClassFileProvider;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.tracereferences.TraceReferencesFormattingConsumer.OutputFormat;
import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FlagFile;
import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
+import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -19,17 +21,6 @@
class TraceReferencesCommandParser {
- enum OutputFormat {
- /** Format used with the -printusage flag */
- PRINTUSAGE,
- /** Keep rules keeping each of the traced references */
- KEEP_RULES,
- /**
- * Keep rules with <code>allowobfuscation</code> modifier keeping each of the traced references
- */
- KEEP_RULES_WITH_ALLOWOBFUSCATION
- }
-
private static final Set<String> OPTIONS_WITH_PARAMETER =
ImmutableSet.of("--lib", "--target", "--source", "--format", "--output");
@@ -78,6 +69,8 @@
private TraceReferencesCommand.Builder parse(
String[] args, Origin origin, TraceReferencesCommand.Builder builder) {
String[] expandedArgs = FlagFile.expandFlagFiles(args, builder::error);
+ Path output = null;
+ OutputFormat format = TraceReferencesFormattingConsumer.OutputFormat.PRINTUSAGE;
for (int i = 0; i < expandedArgs.length; i++) {
String arg = expandedArgs[i].trim();
String nextArg = null;
@@ -103,28 +96,42 @@
} else if (arg.equals("--source")) {
builder.addSourceFiles(Paths.get(nextArg));
} else if (arg.equals("--format")) {
- OutputFormat format = null;
if (nextArg.equals("printuses")) {
- format = OutputFormat.PRINTUSAGE;
+ format = TraceReferencesFormattingConsumer.OutputFormat.PRINTUSAGE;
}
if (nextArg.equals("keep")) {
- format = OutputFormat.KEEP_RULES;
+ format = TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES;
}
if (nextArg.equals("keepallowobfuscation")) {
- format = OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION;
+ format = TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION;
}
if (format == null) {
builder.error(new StringDiagnostic("Unsupported format '" + nextArg + "'"));
}
- builder.setOutputFormat(format);
} else if (arg.equals("--output")) {
- builder.setOutputPath(Paths.get(nextArg));
+ output = Paths.get(nextArg);
} else if (arg.startsWith("@")) {
builder.error(new StringDiagnostic("Recursive @argfiles are not supported: ", origin));
} else {
builder.error(new StringDiagnostic("Unsupported argument '" + arg + "'"));
}
}
+ final Path finalOutput = output;
+ builder.setConsumer(
+ new TraceReferencesFormattingConsumer(format) {
+ @Override
+ public void finished() {
+ PrintStream out = System.out;
+ if (finalOutput != null) {
+ try {
+ out = new PrintStream(Files.newOutputStream(finalOutput));
+ } catch (IOException e) {
+ builder.error(new ExceptionDiagnostic(e));
+ }
+ }
+ out.print(get());
+ }
+ });
return builder;
}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesConsumer.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesConsumer.java
new file mode 100644
index 0000000..38b8086
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesConsumer.java
@@ -0,0 +1,90 @@
+// 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.tracereferences;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.KeepForSubclassing;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.PackageReference;
+
+/** Consumer interface for recording references */
+@KeepForSubclassing
+public interface TraceReferencesConsumer {
+
+ /**
+ * Interface for asking for the access flags for a traced reference when the definition is present
+ */
+ @Keep
+ interface AccessFlags {
+ boolean isStatic();
+
+ boolean isPublic();
+
+ boolean isProtected();
+
+ boolean isPrivate();
+ }
+
+ /**
+ * Interface for asking for additional class information for a traced class when the definition is
+ * found.
+ */
+ @Keep
+ interface ClassAccessFlags extends AccessFlags {
+ boolean isInterface();
+
+ boolean isEnum();
+ }
+
+ @Keep
+ interface FieldAccessFlags extends AccessFlags {}
+
+ @Keep
+ interface MethodAccessFlags extends AccessFlags {}
+
+ /** Interface implemented by all references reported */
+ @Keep
+ interface TracedReference<T, F> {
+ /** Returns if the reference does not have a definition in the program traced. */
+ boolean isMissingDefinition();
+
+ /** Returns the reference traced. */
+ T getReference();
+
+ /**
+ * Returns the access flags for the reference traced. If the definition is not found (<code>
+ * isMissingDefinition()</code> returns <code>true</code>) the access flags are not known and
+ * this returns <code>null</code>.
+ */
+ F getAccessFlags();
+ }
+
+ @Keep
+ interface TracedClass extends TracedReference<ClassReference, ClassAccessFlags> {}
+
+ @Keep
+ interface TracedField extends TracedReference<FieldReference, FieldAccessFlags> {}
+
+ @Keep
+ interface TracedMethod extends TracedReference<MethodReference, MethodAccessFlags> {}
+
+ /** Class has been traced. */
+ void acceptType(TracedClass tracedClazz);
+
+ /** Field has been traced. */
+ void acceptField(TracedField tracedField);
+
+ /** Method has been traced. */
+ void acceptMethod(TracedMethod tracedMethod);
+
+ /** Package which is required for package privatge access has been traced. */
+ void acceptPackage(PackageReference pkg);
+
+ /**
+ * Tracing has finished. There will be no more calls to any of the <code>acceptXXX</code> methods.
+ */
+ void finished();
+}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesFormattingConsumer.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesFormattingConsumer.java
new file mode 100644
index 0000000..67167c4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesFormattingConsumer.java
@@ -0,0 +1,79 @@
+// 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.tracereferences;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.references.PackageReference;
+
+class TraceReferencesFormattingConsumer implements TraceReferencesConsumer {
+
+ public enum OutputFormat {
+ /** Format used with the -printusage flag */
+ PRINTUSAGE,
+ /** Keep rules keeping each of the traced references */
+ KEEP_RULES,
+ /**
+ * Keep rules with <code>allowobfuscation</code> modifier keeping each of the traced references
+ */
+ KEEP_RULES_WITH_ALLOWOBFUSCATION
+ }
+
+ private final OutputFormat format;
+ private final TraceReferencesResult.Builder builder = TraceReferencesResult.builder();
+ private boolean finishedCalled = false;
+
+ public TraceReferencesFormattingConsumer(OutputFormat format) {
+ this.format = format;
+ }
+
+ @Override
+ public void acceptType(TracedClass type) {
+ assert !finishedCalled;
+ builder.acceptType(type);
+ }
+
+ @Override
+ public void acceptField(TracedField field) {
+ assert !finishedCalled;
+ builder.acceptField(field);
+ }
+
+ @Override
+ public void acceptMethod(TracedMethod method) {
+ assert !finishedCalled;
+ builder.acceptMethod(method);
+ }
+
+ @Override
+ public void acceptPackage(PackageReference pkg) {
+ assert !finishedCalled;
+ builder.acceptPackage(pkg);
+ }
+
+ @Override
+ public void finished() {
+ assert !finishedCalled;
+ finishedCalled = true;
+ }
+
+ public String get() {
+ TraceReferencesResult result = builder.build();
+ Formatter formatter;
+ switch (format) {
+ case PRINTUSAGE:
+ formatter = new PrintUsesFormatter();
+ break;
+ case KEEP_RULES:
+ formatter = new KeepRuleFormatter(false);
+ break;
+ case KEEP_RULES_WITH_ALLOWOBFUSCATION:
+ formatter = new KeepRuleFormatter(true);
+ break;
+ default:
+ throw new Unreachable();
+ }
+ formatter.format(result);
+ return formatter.get();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesResult.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesResult.java
new file mode 100644
index 0000000..f8dab44
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesResult.java
@@ -0,0 +1,94 @@
+// 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.tracereferences;
+
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.PackageReference;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedClass;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedField;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedMethod;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+class TraceReferencesResult {
+
+ final Set<TracedClass> types;
+ final Map<ClassReference, Set<TracedField>> fields;
+ final Map<ClassReference, Set<TracedMethod>> methods;
+ final Set<PackageReference> keepPackageNames;
+ final Set<Object> missingDefinition;
+
+ TraceReferencesResult(
+ Set<TracedClass> types,
+ Map<ClassReference, Set<TracedField>> fields,
+ Map<ClassReference, Set<TracedMethod>> methods,
+ Set<PackageReference> keepPackageNames,
+ Set<Object> missingDefinition) {
+ this.types = types;
+ this.fields = fields;
+ this.methods = methods;
+ this.keepPackageNames = keepPackageNames;
+ this.missingDefinition = missingDefinition;
+ }
+
+ static Builder builder() {
+ return new Builder();
+ }
+
+ static class Builder implements TraceReferencesConsumer {
+ private final Set<TracedClass> types = new HashSet<>();
+ private final Map<ClassReference, Set<TracedField>> fields = new HashMap<>();
+ private final Map<ClassReference, Set<TracedMethod>> methods = new HashMap<>();
+ private final Set<Object> missingDefinition = new HashSet<>();
+ private final Set<PackageReference> keepPackageNames = new HashSet<>();
+
+ @Override
+ public void acceptType(TracedClass tracedClass) {
+ types.add(tracedClass);
+ if (tracedClass.isMissingDefinition()) {
+ this.missingDefinition.add(tracedClass.getReference());
+ }
+ }
+
+ @Override
+ public void acceptField(TracedField tracedField) {
+ FieldReference field = tracedField.getReference();
+ fields.computeIfAbsent(field.getHolderClass(), k -> new HashSet<>()).add(tracedField);
+ if (tracedField.isMissingDefinition()) {
+ this.missingDefinition.add(field);
+ }
+ }
+
+ @Override
+ public void acceptMethod(TracedMethod tracedMethod) {
+ MethodReference method = tracedMethod.getReference();
+ methods.computeIfAbsent(method.getHolderClass(), k -> new HashSet<>()).add(tracedMethod);
+ if (tracedMethod.isMissingDefinition()) {
+ this.missingDefinition.add(method);
+ }
+ }
+
+ @Override
+ public void acceptPackage(PackageReference pkg) {
+ keepPackageNames.add(pkg);
+ }
+
+ @Override
+ public void finished() {}
+
+ TraceReferencesResult build() {
+ missingDefinition.forEach(
+ missingDefinition -> {
+ assert missingDefinition instanceof ClassReference
+ || missingDefinition instanceof FieldReference
+ || missingDefinition instanceof MethodReference;
+ });
+ return new TraceReferencesResult(types, fields, methods, keepPackageNames, missingDefinition);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
index 5ad1cfa..d30158b 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
@@ -5,6 +5,7 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -17,7 +18,6 @@
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.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueArray;
@@ -26,29 +26,209 @@
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.shaking.MainDexClasses;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.AccessFlags;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.ClassAccessFlags;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.FieldAccessFlags;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.MethodAccessFlags;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedClass;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedField;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedMethod;
+import com.android.tools.r8.tracereferences.TraceReferencesConsumer.TracedReference;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.Timing;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
+import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Set;
class Tracer {
+ static class AccessFlagsImpl<T extends com.android.tools.r8.graph.AccessFlags<T>>
+ implements AccessFlags {
+ T accessFlags;
+
+ AccessFlagsImpl(T accessFlags) {
+ this.accessFlags = accessFlags;
+ }
+
+ @Override
+ public boolean isStatic() {
+ return accessFlags.isStatic();
+ }
+
+ @Override
+ public boolean isPublic() {
+ return accessFlags.isPublic();
+ }
+
+ @Override
+ public boolean isProtected() {
+ return accessFlags.isProtected();
+ }
+
+ @Override
+ public boolean isPrivate() {
+ return accessFlags.isPrivate();
+ }
+ }
+
+ static class ClassAccessFlagsImpl
+ extends AccessFlagsImpl<com.android.tools.r8.graph.ClassAccessFlags>
+ implements ClassAccessFlags {
+ ClassAccessFlagsImpl(com.android.tools.r8.graph.ClassAccessFlags accessFlags) {
+ super(accessFlags);
+ }
+
+ @Override
+ public boolean isInterface() {
+ return accessFlags.isInterface();
+ }
+
+ @Override
+ public boolean isEnum() {
+ return accessFlags.isEnum();
+ }
+ }
+
+ static class FieldAccessFlagsImpl
+ extends AccessFlagsImpl<com.android.tools.r8.graph.FieldAccessFlags>
+ implements FieldAccessFlags {
+ FieldAccessFlagsImpl(com.android.tools.r8.graph.FieldAccessFlags accessFlags) {
+ super(accessFlags);
+ }
+ }
+
+ static class MethodAccessFlagsImpl
+ extends AccessFlagsImpl<com.android.tools.r8.graph.MethodAccessFlags>
+ implements MethodAccessFlags {
+ MethodAccessFlagsImpl(com.android.tools.r8.graph.MethodAccessFlags accessFlags) {
+ super(accessFlags);
+ }
+ }
+
+ abstract static class TracedReferenceBase<T, F> implements TracedReference<T, F> {
+ private final T reference;
+ private final F accessFlags;
+ private final boolean missingDefinition;
+
+ private TracedReferenceBase(T reference, F accessFlags, boolean missingDefinition) {
+ assert accessFlags != null || missingDefinition;
+ this.reference = reference;
+ this.accessFlags = accessFlags;
+ this.missingDefinition = missingDefinition;
+ }
+
+ @Override
+ public T getReference() {
+ return reference;
+ }
+
+ @Override
+ public boolean isMissingDefinition() {
+ return missingDefinition;
+ }
+
+ @Override
+ public F getAccessFlags() {
+ return accessFlags;
+ }
+
+ @Override
+ public int hashCode() {
+ // Equality is only based on the reference.
+ return reference.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ // Equality is only based on the reference.
+ if (!(other instanceof TracedReferenceBase)) {
+ return false;
+ }
+ return reference.equals(((TracedReferenceBase<?, ?>) other).reference);
+ }
+
+ public abstract String getKindName();
+ }
+
+ static class TracedClassImpl extends TracedReferenceBase<ClassReference, ClassAccessFlags>
+ implements TracedClass {
+ private TracedClassImpl(DexType reference, DexClass definition) {
+ super(
+ Reference.classFromDescriptor(reference.toDescriptorString()),
+ definition != null ? new ClassAccessFlagsImpl(definition.getAccessFlags()) : null,
+ definition == null);
+ }
+
+ @Override
+ public String getKindName() {
+ return "type";
+ }
+
+ @Override
+ public String toString() {
+ return getReference().getTypeName();
+ }
+ }
+
+ static class TracedFieldImpl extends TracedReferenceBase<FieldReference, FieldAccessFlags>
+ implements TracedField {
+ private TracedFieldImpl(DexField reference, DexEncodedField definition) {
+ super(
+ Reference.field(
+ Reference.classFromDescriptor(reference.holder.toDescriptorString()),
+ reference.name.toString(),
+ Reference.typeFromDescriptor(reference.type.toDescriptorString())),
+ definition != null ? new FieldAccessFlagsImpl(definition.getAccessFlags()) : null,
+ definition == null);
+ }
+
+ @Override
+ public String getKindName() {
+ return "field";
+ }
+
+ @Override
+ public String toString() {
+ return getReference().toString();
+ }
+ }
+
+ static class TracedMethodImpl extends TracedReferenceBase<MethodReference, MethodAccessFlags>
+ implements TracedMethod {
+ private TracedMethodImpl(DexMethod reference, DexEncodedMethod definition) {
+ super(
+ reference.asMethodReference(),
+ definition != null ? new MethodAccessFlagsImpl(definition.getAccessFlags()) : null,
+ definition == null);
+ }
+
+ @Override
+ public String getKindName() {
+ return "method";
+ }
+
+ @Override
+ public String toString() {
+ return getReference().toString();
+ }
+ }
+
private final Set<String> descriptors;
- private Set<DexType> types = Sets.newIdentityHashSet();
- private Map<DexType, Set<DexMethod>> methods = Maps.newIdentityHashMap();
- private Map<DexType, Set<DexField>> fields = Maps.newIdentityHashMap();
- private Set<String> keepPackageNames = Sets.newHashSet();
- private Set<DexReference> missingDefinitions = Sets.newHashSet();
+ private final DiagnosticsHandler diagnostics;
private final DirectMappedDexApplication application;
private final AppInfoWithClassHierarchy appInfo;
- Tracer(Set<String> descriptors, AndroidApp inputApp) throws Exception {
+ Tracer(Set<String> descriptors, AndroidApp inputApp, DiagnosticsHandler diagnostics)
+ throws Exception {
this.descriptors = descriptors;
+ this.diagnostics = diagnostics;
InternalOptions options = new InternalOptions();
application =
new ApplicationReader(inputApp, options, new Timing("ReferenceTrace")).read().toDirect();
@@ -59,8 +239,8 @@
MainDexClasses.createEmptyMainDexClasses());
}
- Result run() {
- UseCollector useCollector = new UseCollector(appInfo.dexItemFactory());
+ void run(TraceReferencesConsumer consumer) {
+ UseCollector useCollector = new UseCollector(appInfo.dexItemFactory(), consumer, diagnostics);
for (DexProgramClass clazz : application.classes()) {
useCollector.setContext(clazz);
useCollector.registerSuperType(clazz, clazz.superType);
@@ -70,74 +250,80 @@
clazz.forEachProgramMethod(useCollector::registerMethod);
clazz.forEachField(useCollector::registerField);
}
-
- return new Result(application, types, keepPackageNames, fields, methods, missingDefinitions);
- }
-
- private boolean isTargetType(DexType type) {
- return descriptors.contains(type.toDescriptorString());
- }
-
- private void addType(DexType type) {
- if (isTargetType(type) && types.add(type)) {
- DexClass clazz = appInfo.definitionFor(type);
- if (clazz != null && clazz.accessFlags.isVisibilityDependingOnPackage()) {
- keepPackageNames.add(clazz.type.getPackageName());
- }
- methods.put(type, Sets.newIdentityHashSet());
- fields.put(type, Sets.newIdentityHashSet());
- }
- }
-
- private void addField(DexField field) {
- addType(field.type);
- DexEncodedField baseField = appInfo.resolveField(field).getResolvedField();
- if (baseField != null && baseField.holder() != field.holder) {
- field = baseField.field;
- }
- addType(field.holder);
- if (isTargetType(field.holder)) {
- Set<DexField> typeFields = fields.get(field.holder);
- assert typeFields != null;
- if (baseField != null) {
- if (baseField.accessFlags.isVisibilityDependingOnPackage()) {
- keepPackageNames.add(baseField.holder().getPackageName());
- }
- } else {
- missingDefinitions.add(field);
- }
- typeFields.add(field);
- }
- }
-
- private void addMethod(DexMethod method) {
- addType(method.holder);
- for (DexType parameterType : method.proto.parameters.values) {
- addType(parameterType);
- }
- addType(method.proto.returnType);
- if (isTargetType(method.holder)) {
- Set<DexMethod> typeMethods = methods.get(method.holder);
- assert typeMethods != null;
- DexClass holder = appInfo.definitionForHolder(method);
- DexEncodedMethod definition = method.lookupOnClass(holder);
- if (definition != null) {
- if (definition.accessFlags.isVisibilityDependingOnPackage()) {
- keepPackageNames.add(definition.holder().getPackageName());
- }
- } else {
- missingDefinitions.add(method);
- }
- typeMethods.add(method);
- }
+ consumer.finished();
}
class UseCollector extends UseRegistry {
+ private final TraceReferencesConsumer consumer;
private DexProgramClass context;
+ private final DiagnosticsHandler diagnostics;
+ private Set<TracedReference<?, ?>> missingDefinitionReported = new HashSet<>();
- UseCollector(DexItemFactory factory) {
+ UseCollector(
+ DexItemFactory factory, TraceReferencesConsumer consumer, DiagnosticsHandler diagnostics) {
super(factory);
+ this.consumer = consumer;
+ this.diagnostics = diagnostics;
+ }
+
+ private boolean isTargetType(DexType type) {
+ return descriptors.contains(type.toDescriptorString());
+ }
+
+ private void addType(DexType type) {
+ if (isTargetType(type)) {
+ DexClass clazz = appInfo.definitionFor(type);
+ TracedClassImpl tracedClass = new TracedClassImpl(type, clazz);
+ consumer.acceptType(tracedClass);
+ checkDiagnostics(tracedClass);
+ if (clazz != null && clazz.accessFlags.isVisibilityDependingOnPackage()) {
+ consumer.acceptPackage(Reference.packageFromString(clazz.type.getPackageName()));
+ }
+ }
+ }
+
+ private void addField(DexField field) {
+ addType(field.type);
+ DexEncodedField baseField = appInfo.resolveField(field).getResolvedField();
+ if (baseField != null && baseField.holder() != field.holder) {
+ field = baseField.field;
+ }
+ addType(field.holder);
+ if (isTargetType(field.holder)) {
+ TracedFieldImpl tracedField = new TracedFieldImpl(field, baseField);
+ consumer.acceptField(tracedField);
+ checkDiagnostics(tracedField);
+ if (baseField != null && baseField.accessFlags.isVisibilityDependingOnPackage()) {
+ consumer.acceptPackage(Reference.packageFromString(baseField.holder().getPackageName()));
+ }
+ }
+ }
+
+ private void addMethod(DexMethod method) {
+ addType(method.holder);
+ for (DexType parameterType : method.proto.parameters.values) {
+ addType(parameterType);
+ }
+ addType(method.proto.returnType);
+ if (isTargetType(method.holder)) {
+ DexClass holder = appInfo.definitionForHolder(method);
+ DexEncodedMethod definition = method.lookupOnClass(holder);
+ TracedMethodImpl tracedMethod = new TracedMethodImpl(method, definition);
+ consumer.acceptMethod(tracedMethod);
+ checkDiagnostics(tracedMethod);
+ if (definition != null && definition.accessFlags.isVisibilityDependingOnPackage()) {
+ consumer.acceptPackage(Reference.packageFromString(definition.holder().getPackageName()));
+ }
+ }
+ }
+
+ private void checkDiagnostics(TracedReferenceBase<?, ?> tracedReference) {
+ if (tracedReference.isMissingDefinition() && missingDefinitionReported.add(tracedReference)) {
+ diagnostics.warning(
+ new StringDiagnostic(
+ "Missing definition of " + tracedReference.getKindName() + " " + tracedReference));
+ }
}
public void setContext(DexProgramClass context) {
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
index 95b0fde..a4cef91 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -552,6 +552,27 @@
binaryName.replace(DESCRIPTOR_PACKAGE_SEPARATOR, JAVA_PACKAGE_SEPARATOR));
}
+ /**
+ * Computes the inner name from the outer- and inner descriptors. If outer is not a prefix of the
+ * inner descriptor null is returned. Do not use this method if the relationship between inner and
+ * outer is not reflected in the name.
+ *
+ * @param outerDescriptor the outer descriptor, such as Lfoo/bar/Baz;
+ * @param innerDescriptor the inner descriptor, such as Lfoo/bar/Baz$Qux;
+ * @return the inner name or null, i.e. Qux in the example above
+ */
+ public static String getInnerClassName(String outerDescriptor, String innerDescriptor) {
+ if (innerDescriptor.length() <= outerDescriptor.length()) {
+ return null;
+ }
+ String prefix =
+ outerDescriptor.substring(0, outerDescriptor.length() - 1) + INNER_CLASS_SEPARATOR;
+ if (innerDescriptor.startsWith(prefix)) {
+ return innerDescriptor.substring(prefix.length(), innerDescriptor.length() - 1);
+ }
+ return null;
+ }
+
public static class ModuleAndDescriptor {
private final String module;
private final String descriptor;
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 af11559..091325a 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1280,6 +1280,7 @@
public int verificationSizeLimitInBytesOverride = -1;
public boolean forceIRForCfToCfDesugar =
System.getProperty("com.android.tools.r8.forceIRForCfToCfDesugar") != null;
+ public boolean disableMappingToOriginalProgramVerification = false;
// Flag to allow processing of resources in D8. A data resource consumer still needs to be
// specified.
diff --git a/src/main/java/com/android/tools/r8/utils/PredicateUtils.java b/src/main/java/com/android/tools/r8/utils/PredicateUtils.java
index 880da5e..f219259 100644
--- a/src/main/java/com/android/tools/r8/utils/PredicateUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/PredicateUtils.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.utils;
+import java.util.function.Function;
import java.util.function.Predicate;
public class PredicateUtils {
@@ -20,4 +21,8 @@
public static <T> Predicate<T> not(Predicate<T> predicate) {
return t -> !predicate.test(t);
}
+
+ public static <T, R> Predicate<T> isNull(Function<T, R> func) {
+ return t -> func.apply(t) == null;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java
index cae7430..3c3d37c 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java
@@ -4,17 +4,23 @@
package com.android.tools.r8.utils.collections;
+import java.util.Collection;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
+import java.util.function.BiConsumer;
public class BidirectionalManyToOneMap<K, V> {
private final Map<K, V> backing = new IdentityHashMap<>();
private final Map<V, Set<K>> inverse = new IdentityHashMap<>();
+ public void forEach(BiConsumer<Set<K>, V> consumer) {
+ inverse.forEach((value, keys) -> consumer.accept(keys, value));
+ }
+
public V getOrDefault(K key, V value) {
return backing.getOrDefault(key, value);
}
@@ -39,8 +45,28 @@
return inverse.getOrDefault(value, Collections.emptySet());
}
+ public boolean isEmpty() {
+ return backing.isEmpty();
+ }
+
+ public void remove(K key) {
+ V value = backing.remove(key);
+ if (value != null) {
+ Set<K> keys = inverse.get(value);
+ keys.remove(key);
+ if (keys.isEmpty()) {
+ inverse.remove(value);
+ }
+ }
+ }
+
public void put(K key, V value) {
+ remove(key);
backing.put(key, value);
inverse.computeIfAbsent(value, ignore -> new LinkedHashSet<>()).add(key);
}
+
+ public Collection<V> values() {
+ return backing.values();
+ }
}
diff --git a/src/test/examples/naming101/c.java b/src/test/examples/naming101/c.java
index 7477024..21744bc 100644
--- a/src/test/examples/naming101/c.java
+++ b/src/test/examples/naming101/c.java
@@ -4,5 +4,5 @@
package naming101;
public class c {
- static int i = 1;
+ public static int i = 1;
}
diff --git a/src/test/examples/naming101/d.java b/src/test/examples/naming101/d.java
index 3bb8131..6da1daf 100644
--- a/src/test/examples/naming101/d.java
+++ b/src/test/examples/naming101/d.java
@@ -4,7 +4,7 @@
package naming101;
public class d {
- static int c() {
+ public static int c() {
return c.i;
}
}
diff --git a/src/test/java/com/android/tools/r8/D8TestCompileResult.java b/src/test/java/com/android/tools/r8/D8TestCompileResult.java
index ea8ff8b..1fcd0e7 100644
--- a/src/test/java/com/android/tools/r8/D8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/D8TestCompileResult.java
@@ -4,12 +4,10 @@
package com.android.tools.r8;
import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
public class D8TestCompileResult extends TestCompileResult<D8TestCompileResult, D8TestRunResult> {
- D8TestCompileResult(
- TestState state, AndroidApp app, AndroidApiLevel minApiLevel, OutputMode outputMode) {
+ D8TestCompileResult(TestState state, AndroidApp app, int minApiLevel, OutputMode outputMode) {
super(state, app, minApiLevel, outputMode);
}
diff --git a/src/test/java/com/android/tools/r8/DXTestBuilder.java b/src/test/java/com/android/tools/r8/DXTestBuilder.java
index 49055a1..30917a7 100644
--- a/src/test/java/com/android/tools/r8/DXTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/DXTestBuilder.java
@@ -51,7 +51,7 @@
Path outJar = dxOutputFolder.resolve("output.jar");
List<String> args = new ArrayList<>();
- args.add("--min-sdk-version=" + minApiLevel.getLevel());
+ args.add("--min-sdk-version=" + minApiLevel);
args.add("--output=" + outJar.toString());
args.addAll(injars.stream().map(Path::toString).collect(Collectors.toList()));
ProcessResult result =
diff --git a/src/test/java/com/android/tools/r8/DXTestCompileResult.java b/src/test/java/com/android/tools/r8/DXTestCompileResult.java
index 9cb6482..4a0c42f 100644
--- a/src/test/java/com/android/tools/r8/DXTestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/DXTestCompileResult.java
@@ -5,12 +5,11 @@
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.errors.Unimplemented;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
public class DXTestCompileResult extends TestCompileResult<DXTestCompileResult, DXTestRunResult> {
- DXTestCompileResult(TestState state, AndroidApp app, AndroidApiLevel minApiLevel) {
+ DXTestCompileResult(TestState state, AndroidApp app, int minApiLevel) {
super(state, app, minApiLevel, OutputMode.DexIndexed);
}
diff --git a/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java b/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java
index 73fe9b4..ea6b2f7 100644
--- a/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.errors.Unimplemented;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.io.IOException;
@@ -25,7 +24,7 @@
Path outputJar,
ProcessResult processResult,
String proguardMap,
- AndroidApiLevel minApiLevel,
+ int minApiLevel,
OutputMode outputMode) {
super(state, AndroidApp.builder().addProgramFiles(outputJar).build(), minApiLevel, outputMode);
assert processResult.exitCode == 0;
diff --git a/src/test/java/com/android/tools/r8/GenerateMainDexListRunResult.java b/src/test/java/com/android/tools/r8/GenerateMainDexListRunResult.java
index 0f62c75..634cfe8 100644
--- a/src/test/java/com/android/tools/r8/GenerateMainDexListRunResult.java
+++ b/src/test/java/com/android/tools/r8/GenerateMainDexListRunResult.java
@@ -8,15 +8,18 @@
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.ListUtils;
import java.util.List;
+import java.util.function.Consumer;
public class GenerateMainDexListRunResult
extends SingleTestRunResult<GenerateMainDexListRunResult> {
+ final TestState state;
List<String> mainDexList;
- public GenerateMainDexListRunResult(List<String> mainDexList) {
+ public GenerateMainDexListRunResult(List<String> mainDexList, TestState state) {
super(null, null, null);
this.mainDexList = mainDexList;
+ this.state = state;
}
public List<ClassReference> getMainDexList() {
@@ -29,6 +32,12 @@
});
}
+ public GenerateMainDexListRunResult inspectDiagnosticMessages(
+ Consumer<TestDiagnosticMessages> consumer) {
+ consumer.accept(state.getDiagnosticsMessages());
+ return self();
+ }
+
@Override
protected GenerateMainDexListRunResult self() {
return this;
diff --git a/src/test/java/com/android/tools/r8/GenerateMainDexListTestBuilder.java b/src/test/java/com/android/tools/r8/GenerateMainDexListTestBuilder.java
index 6b5829b..0700a5f 100644
--- a/src/test/java/com/android/tools/r8/GenerateMainDexListTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/GenerateMainDexListTestBuilder.java
@@ -12,6 +12,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.List;
public class GenerateMainDexListTestBuilder
extends TestBaseBuilder<
@@ -26,7 +27,8 @@
}
public static GenerateMainDexListTestBuilder create(TestState state) {
- return new GenerateMainDexListTestBuilder(state, GenerateMainDexListCommand.builder());
+ return new GenerateMainDexListTestBuilder(
+ state, GenerateMainDexListCommand.builder(state.getDiagnosticsHandler()));
}
@Override
@@ -67,7 +69,7 @@
}
public GenerateMainDexListRunResult run() throws CompilationFailedException {
- return new GenerateMainDexListRunResult(GenerateMainDexList.run(builder.build()));
+ return new GenerateMainDexListRunResult(GenerateMainDexList.run(builder.build()), getState());
}
public GenerateMainDexListTestBuilder addMainDexRules(Collection<String> rules) {
@@ -78,4 +80,13 @@
public GenerateMainDexListTestBuilder addMainDexRules(String... rules) {
return addMainDexRules(Arrays.asList(rules));
}
+
+ public GenerateMainDexListTestBuilder addDataResources(List<DataEntryResource> resources) {
+ resources.forEach(builder.getAppBuilder()::addDataResource);
+ return self();
+ }
+
+ public GenerateMainDexListTestBuilder addDataEntryResources(DataEntryResource... resources) {
+ return addDataResources(Arrays.asList(resources));
+ }
}
diff --git a/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java b/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
index b51c598..c352417 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
@@ -5,7 +5,6 @@
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.errors.Unimplemented;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.io.IOException;
@@ -18,8 +17,7 @@
private final Path outputJar;
private final String proguardMap;
- ProguardTestCompileResult(
- TestState state, Path outputJar, AndroidApiLevel minApiLevel, String proguardMap) {
+ ProguardTestCompileResult(TestState state, Path outputJar, int minApiLevel, String proguardMap) {
super(
state,
AndroidApp.builder().addProgramFiles(outputJar).build(),
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index 5e2f078..c03cd49 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -101,7 +101,6 @@
@Override
@Test
public void lambdaDesugaring() throws Throwable {
- expectThrowsWithHorizontalClassMerging();
test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
.withMinApiLevel(ToolHelper.getMinApiLevelForDexVmNoHigherThan(AndroidApiLevel.K))
.withOptionConsumer(opts -> opts.enableClassInlining = false)
@@ -121,7 +120,6 @@
@Test
public void testMultipleInterfacesLambdaOutValue() throws Throwable {
- expectThrowsWithHorizontalClassMerging();
// We can only remove trivial check casts for the lambda objects if we keep track all the
// multiple interfaces we additionally specified for the lambdas
test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
@@ -143,9 +141,6 @@
@Test
@IgnoreIfVmOlderThan(Version.V7_0_0)
public void lambdaDesugaringWithDefaultMethods() throws Throwable {
- // This should be fixed by horizontal class merging field mapping.
- expectThrowsWithHorizontalClassMerging();
-
test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
.withMinApiLevel(AndroidApiLevel.N)
.withOptionConsumer(opts -> opts.enableClassInlining = false)
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 1ad8f28..de56e20 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -452,10 +452,18 @@
}
public T enableMemberValuePropagationAnnotations() {
- if (!enableMemberValuePropagationAnnotations) {
- enableMemberValuePropagationAnnotations = true;
- addInternalKeepRules(
- "-neverpropagatevalue class * { @com.android.tools.r8.NeverPropagateValue *; }");
+ return enableMemberValuePropagationAnnotations(true);
+ }
+
+ public T enableMemberValuePropagationAnnotations(boolean enable) {
+ if (enable) {
+ if (!enableMemberValuePropagationAnnotations) {
+ enableMemberValuePropagationAnnotations = true;
+ addInternalKeepRules(
+ "-neverpropagatevalue class * { @com.android.tools.r8.NeverPropagateValue *; }");
+ }
+ } else {
+ assert !enableMemberValuePropagationAnnotations;
}
return self();
}
diff --git a/src/test/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
index 0deb4f5..85c6b96 100644
--- a/src/test/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.shaking.CollectingGraphConsumer;
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.ProguardConfigurationRule;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.ThrowingConsumer;
@@ -40,7 +39,7 @@
List<ProguardConfigurationRule> syntheticProguardRules,
String proguardMap,
CollectingGraphConsumer graphConsumer,
- AndroidApiLevel minApiLevel,
+ int minApiLevel,
List<Path> features) {
super(state, app, minApiLevel, outputMode);
this.proguardConfiguration = proguardConfiguration;
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index 7dfe4b5..c888c1d 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -45,19 +45,18 @@
import org.hamcrest.Matcher;
public abstract class TestCompileResult<
- CR extends TestCompileResult<CR, RR>, RR extends TestRunResult>
+ CR extends TestCompileResult<CR, RR>, RR extends TestRunResult<RR>>
extends TestBaseResult<CR, RR> {
public final AndroidApp app;
- public final AndroidApiLevel minApiLevel;
+ public final int minApiLevel;
private final OutputMode outputMode;
final List<Path> additionalRunClassPath = new ArrayList<>();
final List<String> vmArguments = new ArrayList<>();
private boolean withArt6Plus64BitsLib = false;
private boolean withArtFrameworks = true;
- TestCompileResult(
- TestState state, AndroidApp app, AndroidApiLevel minApiLevel, OutputMode outputMode) {
+ TestCompileResult(TestState state, AndroidApp app, int minApiLevel, OutputMode outputMode) {
super(state);
this.app = app;
this.minApiLevel = minApiLevel;
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 590f10a..3e5ba5f 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -53,7 +53,7 @@
private final List<Path> additionalRunClassPath = new ArrayList<>();
private ProgramConsumer programConsumer;
private StringConsumer mainDexListConsumer;
- protected AndroidApiLevel minApiLevel = ToolHelper.getMinApiLevelForDexVm();
+ protected int minApiLevel = ToolHelper.getMinApiLevelForDexVm().getLevel();
private Consumer<InternalOptions> optionsConsumer = DEFAULT_OPTIONS;
private ByteArrayOutputStream stdout = null;
private PrintStream oldStdout = null;
@@ -110,7 +110,7 @@
if (backend.isDex() || !isTestShrinkerBuilder()) {
assert !builder.isMinApiLevelSet()
: "Don't set the API level directly through BaseCompilerCommand.Builder in tests";
- builder.setMinApiLevel(minApiLevel.getLevel());
+ builder.setMinApiLevel(minApiLevel);
}
if (useDefaultRuntimeLibrary) {
if (backend == Backend.DEX) {
@@ -238,7 +238,7 @@
}
public T setMinApi(int minApiLevel) {
- this.minApiLevel = AndroidApiLevel.getAndroidApiLevel(minApiLevel);
+ this.minApiLevel = minApiLevel;
return self();
}
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 6d3590d..477aab1 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -174,6 +174,10 @@
return addKeepRules("-keep class " + pkg.getName() + ".*");
}
+ public T addKeepPackageNamesRule(Package pkg) {
+ return addKeepRules("-keeppackagenames " + pkg.getName());
+ }
+
public T addKeepMainRule(Class<?> mainClass) {
return addKeepMainRule(mainClass.getTypeName());
}
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
index aed9abc..32fa0b8 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
@@ -39,7 +39,6 @@
@Test
public void test() throws Exception {
- expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.addInnerClasses(RemoveVisibilityBridgeMethodsTest.class)
.addKeepMainRule(Main.class)
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 4baa16c..b3ff5a3 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
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf.bootstrap;
+import static com.android.tools.r8.graph.GenericSignatureIdentityTest.testParseSignaturesInJar;
import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
import static com.google.common.io.ByteStreams.toByteArray;
import static org.hamcrest.CoreMatchers.anyOf;
@@ -208,6 +209,11 @@
}
@Test
+ public void testSignatures() throws Exception {
+ testParseSignaturesInJar(r8R8Release.getFirst());
+ }
+
+ @Test
public void test() throws Exception {
expectThrowsWithHorizontalClassMerging();
Path helloJar = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "hello" + JAR_EXTENSION);
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java
index 316d547..9783627 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java
@@ -50,7 +50,7 @@
MethodSubject firstInitSubject = aClassSubject.init("int");
assertThat(firstInitSubject, isPresent());
assertThat(
- firstInitSubject, writesInstanceField(classIdFieldSubject.getFieldReference()));
+ firstInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
ClassSubject synthesizedClass = getSynthesizedArgumentClassSubject(codeInspector);
@@ -58,12 +58,11 @@
aClassSubject.init("int", synthesizedClass.getFinalName());
assertThat(otherInitSubject, isPresent());
assertThat(
- otherInitSubject, writesInstanceField(classIdFieldSubject.getFieldReference()));
+ otherInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
MethodSubject printSubject = aClassSubject.method("void", "print");
assertThat(printSubject, isPresent());
- assertThat(
- printSubject, readsInstanceField(classIdFieldSubject.getFieldReference()));
+ assertThat(printSubject, readsInstanceField(classIdFieldSubject.getDexField()));
assertThat(codeInspector.clazz(B.class), not(isPresent()));
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
index b7d3a2d..dd1b6ca 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
@@ -56,18 +56,17 @@
MethodSubject firstInitSubject = aClassSubject.init("int");
assertThat(firstInitSubject, isPresent());
assertThat(
- firstInitSubject, writesInstanceField(classIdFieldSubject.getFieldReference()));
+ firstInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
MethodSubject otherInitSubject =
aClassSubject.init(changedClassSubject.getFinalName(), "int");
assertThat(otherInitSubject, isPresent());
assertThat(
- otherInitSubject, writesInstanceField(classIdFieldSubject.getFieldReference()));
+ otherInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
MethodSubject printSubject = aClassSubject.method("void", "print");
assertThat(printSubject, isPresent());
- assertThat(
- printSubject, readsInstanceField(classIdFieldSubject.getFieldReference()));
+ assertThat(printSubject, readsInstanceField(classIdFieldSubject.getDexField()));
assertThat(codeInspector.clazz(B.class), not(isPresent()));
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java
index 3d13626..fea20ed 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java
@@ -50,7 +50,7 @@
MethodSubject firstInitSubject = aClassSubject.init("int");
assertThat(firstInitSubject, isPresent());
assertThat(
- firstInitSubject, writesInstanceField(classIdFieldSubject.getFieldReference()));
+ firstInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
ClassSubject synthesizedClass = getSynthesizedArgumentClassSubject(codeInspector);
@@ -58,12 +58,11 @@
aClassSubject.init("int", synthesizedClass.getFinalName());
assertThat(otherInitSubject, isPresent());
assertThat(
- otherInitSubject, writesInstanceField(classIdFieldSubject.getFieldReference()));
+ otherInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
MethodSubject printSubject = aClassSubject.method("void", "print");
assertThat(printSubject, isPresent());
- assertThat(
- printSubject, readsInstanceField(classIdFieldSubject.getFieldReference()));
+ assertThat(printSubject, readsInstanceField(classIdFieldSubject.getDexField()));
assertThat(codeInspector.clazz(B.class), not(isPresent()));
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/FieldTypeMergedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/FieldTypeMergedTest.java
new file mode 100644
index 0000000..616c88a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/FieldTypeMergedTest.java
@@ -0,0 +1,112 @@
+// 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.isFieldOfArrayType;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isFieldOfType;
+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.assertTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import org.junit.Test;
+
+public class FieldTypeMergedTest extends HorizontalClassMergingTestBase {
+ public FieldTypeMergedTest(TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("a", "b", "bar", "bar", "bar")
+ .inspect(
+ codeInspector -> {
+ ClassSubject aClassSubject = codeInspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ assertThat(
+ codeInspector.clazz(B.class), notIf(isPresent(), enableHorizontalClassMerging));
+
+ ClassSubject cClassSubject = codeInspector.clazz(C.class);
+ assertThat(codeInspector.clazz(C.class), isPresent());
+
+ FieldSubject fieldSubject = cClassSubject.uniqueFieldWithName("fieldB");
+ assertThat(fieldSubject, isPresent());
+ if (enableHorizontalClassMerging) {
+ assertThat(
+ fieldSubject, isFieldOfType(aClassSubject.getDexProgramClass().getType()));
+ }
+
+ fieldSubject = cClassSubject.uniqueFieldWithName("fieldArrayB");
+ assertThat(fieldSubject, isPresent());
+ assertTrue(fieldSubject.getDexField().type.isArrayType());
+ if (enableHorizontalClassMerging) {
+ assertThat(
+ fieldSubject,
+ isFieldOfArrayType(
+ codeInspector, aClassSubject.getDexProgramClass().getType()));
+ }
+ });
+ }
+
+ @NeverClassInline
+ public static class A {
+ public A() {
+ System.out.println("a");
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ public B() {
+ System.out.println("b");
+ }
+
+ @NeverInline
+ public void bar() {
+ System.out.println("bar");
+ }
+ }
+
+ @NeverClassInline
+ public static class C {
+ B fieldB;
+ B[] fieldArrayB;
+
+ public C(B b) {
+ fieldB = b;
+ fieldArrayB = new B[] {b, b};
+ }
+
+ @NeverInline
+ public void foo() {
+ fieldB.bar();
+ for (B b : fieldArrayB) {
+ b.bar();
+ }
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ new A();
+ B b = new B();
+ new C(b).foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java
index b2e1d59..605ff5e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java
@@ -13,9 +13,6 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.classmerging.horizontal.ConstructorMergingTest.A;
-import com.android.tools.r8.classmerging.horizontal.ConstructorMergingTest.B;
-import com.android.tools.r8.classmerging.horizontal.ConstructorMergingTest.Main;
import com.android.tools.r8.horizontalclassmerging.ClassMerger;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.FieldSubject;
@@ -53,17 +50,16 @@
MethodSubject firstInitSubject = aClassSubject.init("int");
assertThat(firstInitSubject, isPresent());
assertThat(
- firstInitSubject, writesInstanceField(classIdFieldSubject.getFieldReference()));
+ firstInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
MethodSubject otherInitSubject = aClassSubject.init("long", "int");
assertThat(otherInitSubject, isPresent());
assertThat(
- otherInitSubject, writesInstanceField(classIdFieldSubject.getFieldReference()));
+ otherInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
MethodSubject printSubject = aClassSubject.method("void", "print");
assertThat(printSubject, isPresent());
- assertThat(
- printSubject, readsInstanceField(classIdFieldSubject.getFieldReference()));
+ assertThat(printSubject, readsInstanceField(classIdFieldSubject.getDexField()));
assertThat(codeInspector.clazz(B.class), not(isPresent()));
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonMapTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonMapTest.java
new file mode 100644
index 0000000..fa5c6d3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonMapTest.java
@@ -0,0 +1,71 @@
+// 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.desugar.desugaredlibrary.gson;
+
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.gson.TestClasses.TestClass;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class GsonMapTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+ private static final Path GSON_CONFIGURATION =
+ Paths.get("src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/gson.cfg");
+ private static final Path GSON_2_8_1_JAR = Paths.get("third_party/iosched_2019/gson-2.8.1.jar");
+
+ @Parameters(name = "shrinkDesugaredLibrary: {0}, runtime: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ public GsonMapTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testGsonMap() throws Exception {
+ Assume.assumeTrue(requiresEmulatedInterfaceCoreLibDesugaring(parameters));
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ R8TestRunResult runResult =
+ testForR8(parameters.getBackend())
+ .addProgramClassesAndInnerClasses(TestClasses.class)
+ .addProgramFiles(GSON_2_8_1_JAR)
+ .addKeepMainRule(TestClass.class)
+ .addKeepRuleFiles(GSON_CONFIGURATION)
+ .allowUnusedProguardConfigurationRules()
+ .addOptionsModification(opt -> opt.ignoreMissingClasses = true)
+ .allowDiagnosticMessages()
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), TestClass.class);
+ // TODO(b/167649682): Should be always true.
+ // .assertSuccessWithOutputLines("true", "true", "true", "true", "true");
+ if (shrinkDesugaredLibrary) {
+ runResult.assertSuccessWithOutputLines("true", "true", "false", "false", "false");
+ } else {
+ runResult.assertSuccessWithOutputLines("true", "true", "false", "true", "false");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/TestClasses.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/TestClasses.java
new file mode 100644
index 0000000..7b57814
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/TestClasses.java
@@ -0,0 +1,221 @@
+// 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.desugar.desugaredlibrary.gson;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class TestClasses {
+
+ // Program class extending ConcurrentHashMap.
+ static class NullableConcurrentHashMap<K, V> extends ConcurrentHashMap<K, V> {
+ NullableConcurrentHashMap() {
+ super();
+ }
+
+ @SuppressWarnings("NullableProblems")
+ @Override
+ public V put(K key, V value) {
+ if (key == null || value == null) {
+ return null;
+ }
+ return super.put(key, value);
+ }
+
+ @Override
+ public void putAll(Map<? extends K, ? extends V> m) {
+ for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+
+ // Program class extending the library class HashMap, implementing Map.
+ static class NullableHashMap<K, V> extends HashMap<K, V> {
+ NullableHashMap() {
+ super();
+ }
+
+ @SuppressWarnings("NullableProblems")
+ @Override
+ public V put(K key, V value) {
+ if (key == null || value == null) {
+ return null;
+ }
+ return super.put(key, value);
+ }
+
+ @Override
+ public void putAll(Map<? extends K, ? extends V> m) {
+ for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+
+ // Program class implementing Map.
+ static class NullableMap<K, V> implements Map<K, V> {
+ private Map<K, V> map = new HashMap<>();
+
+ NullableMap() {
+ super();
+ }
+
+ @Override
+ public int size() {
+ return map.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return map.containsKey(key);
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ return map.containsValue(value);
+ }
+
+ @Override
+ public V get(Object key) {
+ return map.get(key);
+ }
+
+ @Nullable
+ @Override
+ public V put(K key, V value) {
+ return map.put(key, value);
+ }
+
+ @Override
+ public V remove(Object key) {
+ return map.remove(key);
+ }
+
+ @Override
+ public void putAll(@NotNull Map<? extends K, ? extends V> m) {
+ for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ @Override
+ public void clear() {
+ map.clear();
+ }
+
+ @NotNull
+ @Override
+ public Set<K> keySet() {
+ return map.keySet();
+ }
+
+ @NotNull
+ @Override
+ public Collection<V> values() {
+ return map.values();
+ }
+
+ @NotNull
+ @Override
+ public Set<Entry<K, V>> entrySet() {
+ return map.entrySet();
+ }
+ }
+
+ static class Data {
+ final int id;
+ final String name;
+
+ Data(int id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Data)) return false;
+ Data data = (Data) o;
+ return id == data.id && name.equals(data.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, name);
+ }
+
+ @Override
+ public String toString() {
+ return "Data{" + "id=" + id + ", name='" + name + '\'' + '}';
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ Gson gson = new Gson();
+ HashMap<Integer, Data> hashMap = new HashMap<>();
+ NullableHashMap<Integer, Data> nullableHashMap = new NullableHashMap<>();
+ NullableMap<Integer, Data> nullableMap = new NullableMap<>();
+ ConcurrentHashMap<Integer, Data> concurrentHashMap = new ConcurrentHashMap<>();
+ NullableConcurrentHashMap<Integer, Data> nullableConcurrentHashMap =
+ new NullableConcurrentHashMap<>();
+
+ fillMap(hashMap);
+ fillMap(nullableHashMap);
+ fillMap(nullableMap);
+ fillMap(concurrentHashMap);
+ fillMap(nullableConcurrentHashMap);
+
+ String hashMapJson = gson.toJson(hashMap);
+ String nullableHashMapJson = gson.toJson(nullableHashMap);
+ String nullableMapJson = gson.toJson(nullableMap);
+ String concurrentHashMapJson = gson.toJson(concurrentHashMap);
+ String nullableConcurrentHashMapJson = gson.toJson(nullableConcurrentHashMap);
+
+ Type hashMapType = new TypeToken<HashMap<Integer, Data>>() {}.getType();
+ HashMap<Integer, Data> hashMapDeserialized = gson.fromJson(hashMapJson, hashMapType);
+ Type nullableHashMapType = new TypeToken<HashMap<Integer, Data>>() {}.getType();
+ HashMap<Integer, Data> nullableHashMapDeserialized =
+ gson.fromJson(nullableHashMapJson, nullableHashMapType);
+ Type nullableMapType = new TypeToken<HashMap<Integer, Data>>() {}.getType();
+ HashMap<Integer, Data> nullableMapDeserialized =
+ gson.fromJson(nullableMapJson, nullableMapType);
+ Type concurrentHashMapType = new TypeToken<ConcurrentHashMap<Integer, Data>>() {}.getType();
+ ConcurrentHashMap<Integer, Data> concurrentHashMapDeserialized =
+ gson.fromJson(concurrentHashMapJson, concurrentHashMapType);
+ Type nullableConcurrentHashMapType =
+ new TypeToken<NullableConcurrentHashMap<Integer, Data>>() {}.getType();
+ NullableConcurrentHashMap<Integer, Data> nullableConcurrentHashMapDeserialized =
+ gson.fromJson(nullableConcurrentHashMapJson, nullableConcurrentHashMapType);
+
+ System.out.println(hashMap.equals(hashMapDeserialized));
+ System.out.println(nullableHashMap.equals(nullableHashMapDeserialized));
+ System.out.println(nullableMap.equals(nullableMapDeserialized));
+ System.out.println(concurrentHashMap.equals(concurrentHashMapDeserialized));
+ System.out.println(nullableConcurrentHashMap.equals(nullableConcurrentHashMapDeserialized));
+ }
+
+ public static void fillMap(Map<Integer, Data> map) {
+ map.put(1, new Data(1, "a"));
+ map.put(2, new Data(2, "b"));
+ map.put(3, new Data(3, "c"));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/gson.cfg b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/gson.cfg
new file mode 100644
index 0000000..51f38a6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/gson.cfg
@@ -0,0 +1,32 @@
+# 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.
+
+# Gson uses generic type information stored in a class file when working with fields. R8
+# removes such information by default, so configure it to keep all of it.
+-keepattributes Signature
+-keepattributes EnclosingMethod
+-keepattributes InnerClasses
+
+# For using GSON @Expose annotation
+-keepattributes AnnotationDefault,RuntimeVisibleAnnotations
+
+# Gson specific classes
+-dontwarn sun.misc.Unsafe
+
+#-keep class com.google.gson.stream.** { *; }
+# Application classes that will be serialized/deserialized over Gson
+-keep class com.android.tools.r8.desugar.desugaredlibrary.gson.GsonMapTest.Data { <fields>; }
+-keep class com.android.tools.r8.desugar.desugaredlibrary.gson.GsonMapTest.NullableConcurrentHashMap { <fields>; }
+
+# Prevent R8 from stripping interface information from TypeAdapter, TypeAdapterFactory,
+# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
+-keep class * extends com.google.gson.TypeAdapter
+-keep class * implements com.google.gson.TypeAdapterFactory
+-keep class * implements com.google.gson.JsonSerializer
+-keep class * implements com.google.gson.JsonDeserializer
+
+# Prevent R8 from leaving Data object members always null
+-keepclassmembers,allowobfuscation class * {
+ @com.google.gson.annotations.SerializedName <fields>;
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
index e1d892d..762916e 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
@@ -47,7 +47,6 @@
@Test
public void testR8CompiledWithR8() throws Exception {
- expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.setMinApi(parameters.getApiLevel())
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR)
diff --git a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
index b567056..b134ae5 100644
--- a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
+++ b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
@@ -25,6 +25,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.LazyLoadedDexApplication;
@@ -112,6 +113,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
+ ClassSignature.NO_CLASS_SIGNATURE,
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
diff --git a/src/test/java/com/android/tools/r8/graph/GenericSignatureIdentityTest.java b/src/test/java/com/android/tools/r8/graph/GenericSignatureIdentityTest.java
new file mode 100644
index 0000000..b94f484
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/GenericSignatureIdentityTest.java
@@ -0,0 +1,91 @@
+// 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.objectweb.asm.Opcodes.ASM7;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.Reporter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+
+@RunWith(Parameterized.class)
+public class GenericSignatureIdentityTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public GenericSignatureIdentityTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testAllClassSignature() throws Exception {
+ testParseSignaturesInJar(ToolHelper.R8_WITH_DEPS_JAR);
+ }
+
+ public static void testParseSignaturesInJar(Path jar) throws Exception {
+ GenericSignatureReader genericSignatureReader = new GenericSignatureReader();
+ ZipInputStream inputStream = new ZipInputStream(Files.newInputStream(jar));
+ ZipEntry next = inputStream.getNextEntry();
+ while (next != null) {
+ if (next.getName().endsWith(".class")) {
+ ClassReader classReader = new ClassReader(inputStream);
+ classReader.accept(genericSignatureReader, 0);
+ }
+ next = inputStream.getNextEntry();
+ }
+ }
+
+ private static class GenericSignatureReader extends ClassVisitor {
+
+ public final Set<String> signatures = new HashSet<>();
+ private final DexItemFactory factory = new DexItemFactory();
+
+ private GenericSignatureReader() {
+ super(ASM7);
+ }
+
+ @Override
+ public void visit(
+ int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ if (signature == null) {
+ return;
+ }
+ TestDiagnosticMessagesImpl testDiagnosticMessages = new TestDiagnosticMessagesImpl();
+ ClassSignature classSignature =
+ GenericSignature.parseClassSignature(
+ name, signature, Origin.unknown(), factory, new Reporter(testDiagnosticMessages));
+ assertEquals(signature, classSignature.toString());
+ testDiagnosticMessages.assertNoMessages();
+ }
+ }
+}
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 366e98b..ddff057 100644
--- a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
@@ -43,23 +43,28 @@
@RunWith(Parameterized.class)
public class GenericSignatureTest extends TestBase {
+ private final TestParameters parameters;
+
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withNoneRuntime().build();
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
- public GenericSignatureTest(TestParameters parameters) {}
+ public GenericSignatureTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
@Test
public void test() throws Exception {
AndroidApp app =
- testForD8()
+ testForD8(parameters.getBackend())
.debug()
.addProgramClassesAndInnerClasses(
GenericSignatureTestClassA.class,
GenericSignatureTestClassB.class,
GenericSignatureTestClassCY.class,
GenericSignatureTestClassCYY.class)
+ .setMinApi(parameters.getApiLevel())
.compile()
.app;
AppView<AppInfoWithLiveness> appView = computeAppViewWithLiveness(app);
@@ -103,13 +108,7 @@
// class <T:GenericSignatureTestClassA<T>.Y>CYY<T extends A<T>.Y> extends CY<T>
DexClass clazz = cyy.getDexProgramClass();
assertNotNull(clazz);
- classSignature =
- GenericSignature.parseClassSignature(
- clazz.getType().getName(),
- getGenericSignature(clazz, appView),
- clazz.origin,
- appView.dexItemFactory(),
- appView.options().reporter);
+ classSignature = clazz.classSignature;
assertNotNull(classSignature);
assertEquals(1, classSignature.formalTypeParameters.size());
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/ClassSignatureTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/ClassSignatureTest.java
new file mode 100644
index 0000000..fade8ed
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/ClassSignatureTest.java
@@ -0,0 +1,177 @@
+// 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.ClassSignature.NO_CLASS_SIGNATURE;
+import static com.google.common.base.Predicates.alwaysFalse;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+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.ClassSignature;
+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 ClassSignatureTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public ClassSignatureTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testSuperClass() {
+ testParsingAndPrintingEqual("Lfoo/bar/baz;");
+ }
+
+ @Test
+ public void testSuperClassWithInnerClasses() {
+ testParsingAndPrintingEqual("Lfoo/bar/baz.Foo.Bar;");
+ }
+
+ @Test
+ public void testSuperClassWithInterfaces1() {
+ testParsingAndPrintingEqual("Lfoo/bar/baz;Lfoo/bar/qux;");
+ }
+
+ @Test
+ public void testSuperClassWithInterfaces2() {
+ testParsingAndPrintingEqual("Lfoo/bar/baz;Lfoo/bar/qux;Lfoo/bar/quux;");
+ }
+
+ @Test
+ public void testSuperClassWithInterfacesInnerClassesSeparatedByDollar() {
+ testParsingAndPrintingEqual("Lfoo/bar/baz$Foo$Bar;Lfoo/bar/qux;Lfoo/bar/quux$Foo$Bar;");
+ }
+
+ @Test
+ public void testSuperClassWithInterfacesInnerClassesSeparatedByPeriod() {
+ testParsingAndPrintingEqual("Lfoo/bar/baz.Foo.Bar;Lfoo/bar/qux;Lfoo/bar/quux.Foo.Bar;");
+ }
+
+ @Test
+ public void testInnerClassesWithSeparatorInName1() {
+ testParsingAndPrintingEqual("Lfoo/bar/baz<*>.Foo$$$<*>.Bar;");
+ }
+
+ @Test
+ public void testInnerClassesWithSeparatorInName2() {
+ testParsingAndPrintingEqual("Lfoo/bar/baz<*>.Foo$Bar$$<*>.Qux;");
+ }
+
+ @Test
+ public void testSuperClassWithInnerClassesGenericArguments2() {
+ testParsingAndPrintingEqual("Lfoo/bar/baz<LFoo;>.Foo<LFoo;>.Bar<LFoo;>;");
+ }
+
+ @Test
+ public void testSuperClassWithInterfacesInnerClassesGenericArguments2() {
+ testParsingAndPrintingEqual(
+ "Lfoo/bar/baz<LFoo;>.Foo<LFoo;>.Bar<LFoo;>;Lfoo/bar/qux<LFoo;>;Lfoo/bar/quux<LFoo;>.Foo<LFoo;>.Bar<LFoo;>;");
+ }
+
+ @Test
+ public void testSuperClassWithInterfacesInnerClassesGenericArguments3() {
+ testParsingAndPrintingEqual(
+ "Lfoo/bar/baz<LFoo;[[I[[[LBar<LFoo;>;>.Foo<LFoo;>.Bar<LFoo;>;Lfoo/bar/qux<LFoo;>;Lfoo/bar/quux<LFoo;>.Foo<LFoo;>.Bar<LFoo;>;");
+ }
+
+ @Test
+ public void testWildCards() {
+ testParsingAndPrintingEqual("Lfoo/Bar<*>;");
+ }
+
+ @Test
+ public void testWildCardsPositiveNegative() {
+ testParsingAndPrintingEqual("Lfoo/Bar<*+[I-LFoo<+LBar;>;>;");
+ }
+
+ @Test
+ public void testSuperClassError() {
+ TestDiagnosticMessages testDiagnosticMessages = testParsingAndPrintingError("Lfoo/bar/baz");
+ testDiagnosticMessages.assertAllWarningsMatch(
+ diagnosticMessage(containsString("Invalid signature 'Lfoo/bar/baz'")));
+ }
+
+ @Test
+ public void testReferenceToTypeVariable() {
+ testParsingAndPrintingEqual("Lfoo/bar/baz<TT;>;");
+ }
+
+ @Test
+ public void testFormalTypeParameters() {
+ testParsingAndPrintingEqual("<T:Ljava/lang/Object;>Lfoo/bar/baz<TT;>;");
+ }
+
+ @Test
+ public void testFormalTypeWithEmpty() {
+ testParsingAndPrintingEqual("<T:>Lfoo/bar/baz<TT;>;");
+ }
+
+ @Test
+ public void testFormalTypeParametersWithInterfaces() {
+ testParsingAndPrintingEqual("<T:Ljava/lang/Object;:LI;>Lfoo/bar/baz<TT;>;");
+ }
+
+ @Test
+ public void testFormalTypeParametersArguments() {
+ testParsingAndPrintingEqual(
+ "<T:Ljava/lang/Object;:LI;R:LFoo<TT;[Lfoo/bar<TT;>.Baz<TT;[I>;>;>Lfoo/bar/baz<TT;>;");
+ }
+
+ @Test
+ public void testFormalTypeParameters2() {}
+
+ @Test
+ public void testFormalTypeParametersEmptyError() {
+ // TODO(b/169716723): This should throw an error
+ assertThrows(AssertionError.class, () -> testParsingAndPrintingError("<>Lfoo/bar/baz<TT;>;"));
+ }
+
+ private void testParsingAndPrintingEqual(String signature) {
+ ClassSignature parsed =
+ GenericSignature.parseClassSignature(
+ "A", signature, Origin.unknown(), new DexItemFactory(), new Reporter());
+ GenericSignaturePrinter genericSignaturePrinter =
+ new GenericSignaturePrinter(NamingLens.getIdentityLens(), alwaysFalse());
+ genericSignaturePrinter.visitClassSignature(parsed);
+ String outSignature = genericSignaturePrinter.toString();
+ assertEquals(signature, outSignature);
+ }
+
+ private TestDiagnosticMessages testParsingAndPrintingError(String signature) {
+ TestDiagnosticMessagesImpl testDiagnosticMessages = new TestDiagnosticMessagesImpl();
+ ClassSignature parsed =
+ GenericSignature.parseClassSignature(
+ "A",
+ signature,
+ Origin.unknown(),
+ new DexItemFactory(),
+ new Reporter(testDiagnosticMessages));
+ assertEquals(NO_CLASS_SIGNATURE, parsed);
+ return testDiagnosticMessages;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java b/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java
index 0465bc5..99b186f 100644
--- a/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramMethod;
@@ -35,6 +36,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
+ ClassSignature.NO_CLASS_SIGNATURE,
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
index 0028070..dec2ff2 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
@@ -41,7 +41,6 @@
@Test
public void testJstyleRunnable() throws Exception {
- expectThrowsWithHorizontalClassMerging();
final String folder = "lambdas_jstyle_runnable";
final String mainClassName = "lambdas_jstyle_runnable.MainKt";
final String implementer1ClassName = "lambdas_jstyle_runnable.Implementer1Kt";
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index e03e056..fd09ef3 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -46,6 +46,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.MethodAccessFlags;
@@ -830,6 +831,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
+ ClassSignature.NO_CLASS_SIGNATURE,
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
diff --git a/src/test/java/com/android/tools/r8/naming/MinifierClassSignatureTest.java b/src/test/java/com/android/tools/r8/naming/MinifierClassSignatureTest.java
index b2ec374..e3e965c 100644
--- a/src/test/java/com/android/tools/r8/naming/MinifierClassSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/naming/MinifierClassSignatureTest.java
@@ -322,6 +322,9 @@
ProguardKeepAttributes.SIGNATURE)
.addKeepAllClassesRuleWithAllowObfuscation()
.allowDiagnosticMessages()
+ .addOptionsModification(
+ internalOptions ->
+ internalOptions.testing.disableMappingToOriginalProgramVerification = true)
.compile();
compileResult.assertNoInfoMessages();
diff --git a/src/test/java/com/android/tools/r8/naming/OverloadedReservedFieldNamingTest.java b/src/test/java/com/android/tools/r8/naming/OverloadedReservedFieldNamingTest.java
index 11c9154..2e211ad 100644
--- a/src/test/java/com/android/tools/r8/naming/OverloadedReservedFieldNamingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/OverloadedReservedFieldNamingTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -57,7 +58,8 @@
ClassSubject classSubject = inspector.clazz(A.class);
assertThat(classSubject, isPresent());
- FieldSubject fieldSubject = classSubject.uniqueFieldWithName("a");
+ FieldSubject fieldSubject =
+ classSubject.asFoundClassSubject().uniqueFieldWithName("a", Reference.BOOL);
assertThat(fieldSubject, isPresentAndNotRenamed());
FieldSubject helloFieldSubject = classSubject.uniqueFieldWithName("hello");
diff --git a/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java b/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
index caa1ae9..66ec3bb 100644
--- a/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
@@ -4,27 +4,33 @@
package com.android.tools.r8.naming;
import static com.android.tools.r8.naming.ClassNameMinifier.getParentPackagePrefix;
-import static com.android.tools.r8.utils.DescriptorUtils.getPackageNameFromDescriptor;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
-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.utils.ListUtils;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.ThrowingConsumer;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.base.CharMatcher;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
+import java.io.File;
+import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.function.BiConsumer;
import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -32,36 +38,16 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class PackageNamingTest extends NamingTestBase {
+public class PackageNamingTest extends TestBase {
- public PackageNamingTest(
- String test,
- List<String> keepRulesFiles,
- BiConsumer<DexItemFactory, NamingLens> inspection) {
- super(test, keepRulesFiles, inspection);
- }
-
- @Test
- public void packageNamingTest() throws Exception {
- NamingLens naming = runMinifier(ListUtils.map(keepRulesFiles, Paths::get));
- inspection.accept(naming);
- }
-
- private static final List<String> CLASSES_IN_NAMING044 = ImmutableList.of(
- "Lnaming044/A;", "Lnaming044/B;", "Lnaming044/sub/SubA;", "Lnaming044/sub/SubB;");
-
- private static final List<String> CLASSES_IN_NAMING101 = ImmutableList.of(
- "Lnaming101/c;", "Lnaming101/d;",
- "Lnaming101/a/a;", "Lnaming101/a/c;",
- "Lnaming101/a/b/c",
- "Lnaming101/b/a'", "Lnaming101/b/b;"
- );
+ private final Path input;
+ private final Path keepRulesFile;
+ private final ThrowingConsumer<CodeInspector, RuntimeException> inspection;
@Parameters(name = "test: {0} keep: {1}")
public static Collection<Object[]> data() {
List<String> tests = Arrays.asList("naming044", "naming101");
-
- Map<String, BiConsumer<DexItemFactory, NamingLens>> inspections = new HashMap<>();
+ Map<String, ThrowingConsumer<CodeInspector, RuntimeException>> inspections = new HashMap<>();
inspections.put("naming044:keep-rules-001.txt", PackageNamingTest::test044_rule001);
inspections.put("naming044:keep-rules-002.txt", PackageNamingTest::test044_rule002);
inspections.put("naming044:keep-rules-003.txt", PackageNamingTest::test044_rule003);
@@ -75,322 +61,361 @@
inspections.put("naming101:keep-rules-102.txt", PackageNamingTest::test101_rule102);
inspections.put("naming101:keep-rules-104.txt", PackageNamingTest::test101_rule104);
inspections.put("naming101:keep-rules-106.txt", PackageNamingTest::test101_rule106);
-
return createTests(tests, inspections);
}
- private static int countPackageDepth(String descriptor) {
- return CharMatcher.is('/').countIn(descriptor);
+ public PackageNamingTest(
+ String test,
+ Path keepRulesFile,
+ ThrowingConsumer<CodeInspector, RuntimeException> inspection) {
+ this.input = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + test + ".jar");
+ this.keepRulesFile = keepRulesFile;
+ this.inspection = inspection;
}
- private static void verifyUniqueNaming(
- DexItemFactory dexItemFactory,
- NamingLens naming,
- List<String> klasses) {
+ @Test
+ public void packageNamingTest() throws Exception {
+ testForR8(Backend.DEX)
+ .addProgramFiles(input)
+ .addKeepRuleFiles(keepRulesFile)
+ .setMinApi(21)
+ .compile()
+ .inspect(inspection);
+ }
+
+ private static final List<String> CLASSES_IN_NAMING044 =
+ ImmutableList.of("naming044.A", "naming044.B", "naming044.sub.SubA", "naming044.sub.SubB");
+
+ private static final List<String> CLASSES_IN_NAMING101 =
+ ImmutableList.of(
+ "naming101.c",
+ "naming101.d",
+ "naming101.a.a",
+ "naming101.a.c",
+ "naming101.a.b.c",
+ "naming101.b.a'",
+ "naming101.b.b");
+
+ private static List<Object[]> createTests(
+ List<String> tests,
+ Map<String, ThrowingConsumer<CodeInspector, RuntimeException>> inspections) {
+ List<Object[]> testCases = new ArrayList<>();
+ Set<String> usedInspections = new HashSet<>();
+ for (String test : tests) {
+ File[] keepFiles =
+ new File(ToolHelper.EXAMPLES_DIR + test)
+ .listFiles(file -> file.isFile() && file.getName().endsWith(".txt"));
+ for (File keepFile : keepFiles) {
+ String keepName = keepFile.getName();
+ ThrowingConsumer<CodeInspector, RuntimeException> inspection =
+ getTestOptionalParameter(inspections, usedInspections, test, keepName);
+ if (inspection != null) {
+ testCases.add(new Object[] {test, keepFile.toPath(), inspection});
+ }
+ }
+ }
+ assert usedInspections.size() == inspections.size();
+ return testCases;
+ }
+
+ private static <T> T getTestOptionalParameter(
+ Map<String, T> specifications, Set<String> usedSpecifications, String test, String keepName) {
+ T parameter = specifications.get(test);
+ if (parameter == null) {
+ parameter = specifications.get(test + ":" + keepName);
+ if (parameter != null) {
+ usedSpecifications.add(test + ":" + keepName);
+ }
+ } else {
+ usedSpecifications.add(test);
+ }
+ return parameter;
+ }
+
+ private static int countPackageDepth(ClassSubject subject) {
+ return CharMatcher.is('.').countIn(subject.getFinalName());
+ }
+
+ private static void verifyUniqueNaming(CodeInspector inspector, List<String> klasses) {
Set<String> renamedNames = Sets.newHashSet();
for (String klass : klasses) {
- DexType k = dexItemFactory.createType(klass);
- String rename = naming.lookupDescriptor(k).toSourceString();
- assertFalse(renamedNames.contains(rename));
- renamedNames.add(rename);
+ String finalName = inspector.clazz(klass).getFinalName();
+ assertFalse(renamedNames.contains(finalName));
+ renamedNames.add(finalName);
}
}
// repackageclasses ''
- private static void test044_rule001(DexItemFactory dexItemFactory, NamingLens naming) {
- verifyUniqueNaming(dexItemFactory, naming, CLASSES_IN_NAMING044);
+ private static void test044_rule001(CodeInspector inspector) {
+ verifyUniqueNaming(inspector, CLASSES_IN_NAMING044);
// All classes are moved to the top-level package, hence no package separator.
- DexType b = dexItemFactory.createType("Lnaming044/B;");
- assertFalse(naming.lookupDescriptor(b).toSourceString().contains("/"));
+ ClassSubject bClassSubject = inspector.clazz("naming044.B");
+ assertFalse(bClassSubject.getFinalName().contains("."));
// Even classes in a sub-package are moved to the same top-level package.
- DexType sub = dexItemFactory.createType("Lnaming044/sub/SubB;");
- assertFalse(naming.lookupDescriptor(sub).toSourceString().contains("/"));
+ assertFalse(inspector.clazz("naming044.sub.SubB").getFinalName().contains("."));
- // method naming044.B.m would be renamed.
- DexMethod m = dexItemFactory.createMethod(
- b, dexItemFactory.createProto(dexItemFactory.intType), "m");
- assertNotEquals("m", naming.lookupName(m).toSourceString());
+ // Method naming044.B.m would be renamed.
+ MethodSubject mMethodSubject = bClassSubject.uniqueMethodWithName("m");
+ assertNotEquals("m", mMethodSubject.getMethod().getName().toSourceString());
}
// repackageclasses 'p44.x'
- private static void test044_rule002(DexItemFactory dexItemFactory, NamingLens naming) {
- verifyUniqueNaming(dexItemFactory, naming, CLASSES_IN_NAMING044);
+ private static void test044_rule002(CodeInspector inspector) {
+ verifyUniqueNaming(inspector, CLASSES_IN_NAMING044);
// All classes are moved to a single package, so they all have the same package prefix.
- DexType a = dexItemFactory.createType("Lnaming044/A;");
- assertTrue(naming.lookupDescriptor(a).toSourceString().startsWith("Lp44/x/"));
+ assertThat(inspector.clazz("naming044.A").getFinalName(), startsWith("p44.x."));
- DexType b = dexItemFactory.createType("Lnaming044/B;");
- assertTrue(naming.lookupDescriptor(b).toSourceString().startsWith("Lp44/x/"));
+ ClassSubject bClassSubject = inspector.clazz("naming044.B");
+ assertThat(bClassSubject.getFinalName(), startsWith("p44.x."));
- DexType sub = dexItemFactory.createType("Lnaming044/sub/SubB;");
- assertTrue(naming.lookupDescriptor(sub).toSourceString().startsWith("Lp44/x/"));
+ ClassSubject subBClassSubject = inspector.clazz("naming044.sub.SubB");
+ assertThat(subBClassSubject.getFinalName(), startsWith("p44.x."));
+
// Even classes in a sub-package are moved to the same package.
assertEquals(
- getPackageNameFromDescriptor(naming.lookupDescriptor(sub).toSourceString()),
- getPackageNameFromDescriptor(naming.lookupDescriptor(b).toSourceString()));
+ subBClassSubject.getDexProgramClass().getType().getPackageName(),
+ bClassSubject.getDexProgramClass().getType().getPackageName());
}
// flattenpackagehierarchy ''
- private static void test044_rule003(DexItemFactory dexItemFactory, NamingLens naming) {
- verifyUniqueNaming(dexItemFactory, naming, CLASSES_IN_NAMING044);
+ private static void test044_rule003(CodeInspector inspector) {
+ verifyUniqueNaming(inspector, CLASSES_IN_NAMING044);
// All packages are moved to the top-level package, hence only one package separator.
- DexType b = dexItemFactory.createType("Lnaming044/B;");
- assertEquals(1, countPackageDepth(naming.lookupDescriptor(b).toSourceString()));
+ ClassSubject b = inspector.clazz("naming044.B");
+ ClassSubject bClassSubject = inspector.clazz("naming044.B");
+ assertEquals(1, countPackageDepth(bClassSubject));
// Classes in a sub-package are moved to the top-level as well, but in a different one.
- DexType sub = dexItemFactory.createType("Lnaming044/sub/SubB;");
- assertEquals(1, countPackageDepth(naming.lookupDescriptor(sub).toSourceString()));
+ ClassSubject sub = inspector.clazz("naming044.sub.SubB");
+ assertEquals(1, countPackageDepth(sub));
assertNotEquals(
- getPackageNameFromDescriptor(naming.lookupDescriptor(sub).toSourceString()),
- getPackageNameFromDescriptor(naming.lookupDescriptor(b).toSourceString()));
+ sub.getDexProgramClass().getType().getPackageName(),
+ b.getDexProgramClass().getType().getPackageName());
// method naming044.B.m would be renamed.
- DexMethod m = dexItemFactory.createMethod(
- b, dexItemFactory.createProto(dexItemFactory.intType), "m");
- assertNotEquals("m", naming.lookupName(m).toSourceString());
+ MethodSubject mMethodSubject = b.uniqueMethodWithName("m");
+ assertNotEquals("m", mMethodSubject.getMethod().getName().toSourceString());
}
// flattenpackagehierarchy 'p44.y'
- private static void test044_rule004(DexItemFactory dexItemFactory, NamingLens naming) {
+ private static void test044_rule004(CodeInspector inspector) {
// All packages are moved to a single package.
- DexType a = dexItemFactory.createType("Lnaming044/A;");
- assertTrue(naming.lookupDescriptor(a).toSourceString().startsWith("Lp44/y/"));
+ ClassSubject a = inspector.clazz("naming044.A");
+ assertTrue(a.getFinalName().startsWith("p44.y."));
// naming004.A -> Lp44/y/a/a;
- assertEquals(3, countPackageDepth(naming.lookupDescriptor(a).toSourceString()));
+ assertEquals(3, countPackageDepth(a));
- DexType b = dexItemFactory.createType("Lnaming044/B;");
- assertTrue(naming.lookupDescriptor(b).toSourceString().startsWith("Lp44/y/"));
+ ClassSubject b = inspector.clazz("naming044.B");
+ assertTrue(b.getFinalName().startsWith("p44.y."));
// naming004.B -> Lp44/y/a/b;
- assertEquals(3, countPackageDepth(naming.lookupDescriptor(b).toSourceString()));
+ assertEquals(3, countPackageDepth(b));
- DexType sub = dexItemFactory.createType("Lnaming044/sub/SubB;");
- assertTrue(naming.lookupDescriptor(sub).toSourceString().startsWith("Lp44/y/"));
+ ClassSubject sub = inspector.clazz("naming044.sub.SubB");
+ assertTrue(sub.getFinalName().startsWith("p44.y."));
// naming004.sub.SubB -> Lp44/y/b/b;
- assertEquals(3, countPackageDepth(naming.lookupDescriptor(sub).toSourceString()));
+ assertEquals(3, countPackageDepth(sub));
// Classes in a sub-package should be in a different package.
assertNotEquals(
- getPackageNameFromDescriptor(naming.lookupDescriptor(sub).toSourceString()),
- getPackageNameFromDescriptor(naming.lookupDescriptor(b).toSourceString()));
+ sub.getDexProgramClass().getType().getPackageName(),
+ b.getDexProgramClass().getType().getPackageName());
}
- private static void test044_rule005(DexItemFactory dexItemFactory, NamingLens naming) {
- verifyUniqueNaming(dexItemFactory, naming, CLASSES_IN_NAMING044);
+ private static void test044_rule005(CodeInspector inspector) {
+ verifyUniqueNaming(inspector, CLASSES_IN_NAMING044);
// All packages are renamed somehow. Need to check package hierarchy is consistent.
- DexType a = dexItemFactory.createType("Lnaming044/A;");
- assertEquals(1, countPackageDepth(naming.lookupDescriptor(a).toSourceString()));
- DexType b = dexItemFactory.createType("Lnaming044/B;");
- assertEquals(1, countPackageDepth(naming.lookupDescriptor(b).toSourceString()));
+ ClassSubject a = inspector.clazz("naming044.A");
+ assertEquals(1, countPackageDepth(a));
+ ClassSubject b = inspector.clazz("naming044.B");
+ assertEquals(1, countPackageDepth(b));
assertEquals(
- getPackageNameFromDescriptor(naming.lookupDescriptor(a).toSourceString()),
- getPackageNameFromDescriptor(naming.lookupDescriptor(b).toSourceString()));
+ a.getDexProgramClass().getType().getPackageName(),
+ b.getDexProgramClass().getType().getPackageName());
- DexType sub_a = dexItemFactory.createType("Lnaming044/sub/SubA;");
- assertEquals(2, countPackageDepth(naming.lookupDescriptor(sub_a).toSourceString()));
- DexType sub_b = dexItemFactory.createType("Lnaming044/sub/SubB;");
- assertEquals(2, countPackageDepth(naming.lookupDescriptor(sub_b).toSourceString()));
+ ClassSubject sub_a = inspector.clazz("naming044.sub.SubA");
+ assertEquals(2, countPackageDepth(sub_a));
+ ClassSubject sub_b = inspector.clazz("naming044.sub.SubB");
+ assertEquals(2, countPackageDepth(sub_b));
assertEquals(
- getPackageNameFromDescriptor(naming.lookupDescriptor(sub_a).toSourceString()),
- getPackageNameFromDescriptor(naming.lookupDescriptor(sub_b).toSourceString()));
+ sub_a.getDexProgramClass().getType().getPackageName(),
+ sub_b.getDexProgramClass().getType().getPackageName());
- // Lnaming044/B -> La/c --prefix--> La
- // Lnaming044/sub/SubB -> La/b/b --prefix--> La/b --prefix--> La
+ // Lnaming044.B -> La/c --prefix--> La
+ // Lnaming044.sub.SubB -> La/b/b --prefix--> La/b --prefix--> La
assertEquals(
- getParentPackagePrefix(naming.lookupDescriptor(b).toSourceString()),
- getParentPackagePrefix(
- getParentPackagePrefix(naming.lookupDescriptor(sub_b).toSourceString())));
+ getParentPackagePrefix(b.getFinalName()),
+ getParentPackagePrefix(getParentPackagePrefix(sub_b.getFinalName())));
}
- private static void test101_rule001(DexItemFactory dexItemFactory, NamingLens naming) {
- verifyUniqueNaming(dexItemFactory, naming, CLASSES_IN_NAMING101);
+ private static void test101_rule001(CodeInspector inspector) {
+ verifyUniqueNaming(inspector, CLASSES_IN_NAMING101);
// All classes are moved to the top-level package, hence no package separator.
- DexType c = dexItemFactory.createType("Lnaming101/c;");
- assertFalse(naming.lookupDescriptor(c).toSourceString().contains("/"));
+ ClassSubject c = inspector.clazz("naming101.c");
+ assertFalse(c.getFinalName().contains("."));
- DexType abc = dexItemFactory.createType("Lnaming101/a/b/c;");
- assertFalse(naming.lookupDescriptor(abc).toSourceString().contains("/"));
- assertNotEquals(
- naming.lookupDescriptor(abc).toSourceString(),
- naming.lookupDescriptor(c).toSourceString());
+ ClassSubject abc = inspector.clazz("naming101.a.b.c");
+ assertFalse(abc.getFinalName().contains("."));
+ assertNotEquals(abc.getFinalName(), c.getFinalName());
}
- private static void test101_rule002(DexItemFactory dexItemFactory, NamingLens naming) {
- verifyUniqueNaming(dexItemFactory, naming, CLASSES_IN_NAMING101);
+ private static void test101_rule002(CodeInspector inspector) {
+ verifyUniqueNaming(inspector, CLASSES_IN_NAMING101);
// Check naming101.*.a is kept due to **.a
- DexType aa = dexItemFactory.createType("Lnaming101/a/a;");
- assertEquals("Lnaming101/a/a;", naming.lookupDescriptor(aa).toSourceString());
- DexType ba = dexItemFactory.createType("Lnaming101/b/a;");
- assertEquals("Lnaming101/b/a;", naming.lookupDescriptor(ba).toSourceString());
+ ClassSubject aa = inspector.clazz("naming101.a.a");
+ assertEquals("naming101.a.a", aa.getFinalName());
+ ClassSubject ba = inspector.clazz("naming101.b.a");
+ assertEquals("naming101.b.a", ba.getFinalName());
// Repackaged to naming101.a, but naming101.a.a exists to make a name conflict.
// Thus, everything else should not be renamed to 'a',
// except for naming101.b.a, which is also kept due to **.a
- List<String> klasses = ImmutableList.of(
- "Lnaming101/c;",
- "Lnaming101/d;",
- "Lnaming101/a/c;",
- "Lnaming101/a/b/c;",
- "Lnaming101/b/b;");
+ List<String> klasses =
+ ImmutableList.of(
+ "naming101.c", "naming101.d", "naming101.a.c", "naming101.a.b.c", "naming101.b.b");
for (String klass : klasses) {
- DexType k = dexItemFactory.createType(klass);
- String renamedName = naming.lookupDescriptor(k).toSourceString();
- assertEquals("naming101.a", getPackageNameFromDescriptor(renamedName));
- assertNotEquals("Lnaming101/a/a;", renamedName);
+ ClassSubject k = inspector.clazz(klass);
+ assertEquals("naming101.a", k.getDexProgramClass().getType().getPackageName());
+ assertNotEquals("naming101.a.a", k.getFinalName());
}
}
- private static void test101_rule003(DexItemFactory dexItemFactory, NamingLens naming) {
- verifyUniqueNaming(dexItemFactory, naming, CLASSES_IN_NAMING101);
+ private static void test101_rule003(CodeInspector inspector) {
+ verifyUniqueNaming(inspector, CLASSES_IN_NAMING101);
// All packages are moved to the top-level package, hence only one package separator.
- DexType aa = dexItemFactory.createType("Lnaming101/a/a;");
- assertEquals(1, countPackageDepth(naming.lookupDescriptor(aa).toSourceString()));
+ ClassSubject aa = inspector.clazz("naming101.a.a");
+ assertEquals(1, countPackageDepth(aa));
- DexType ba = dexItemFactory.createType("Lnaming101/b/a;");
- assertEquals(1, countPackageDepth(naming.lookupDescriptor(ba).toSourceString()));
+ ClassSubject ba = inspector.clazz("naming101.b.a");
+ assertEquals(1, countPackageDepth(ba));
assertNotEquals(
- getPackageNameFromDescriptor(naming.lookupDescriptor(aa).toSourceString()),
- getPackageNameFromDescriptor(naming.lookupDescriptor(ba).toSourceString()));
+ aa.getDexProgramClass().getType().getPackageName(),
+ ba.getDexProgramClass().getType().getPackageName());
}
- private static void test101_rule004(DexItemFactory dexItemFactory, NamingLens naming) {
- verifyUniqueNaming(dexItemFactory, naming, CLASSES_IN_NAMING101);
+ private static void test101_rule004(CodeInspector inspector) {
+ verifyUniqueNaming(inspector, CLASSES_IN_NAMING101);
// Check naming101.*.a is kept due to **.a
- DexType aa = dexItemFactory.createType("Lnaming101/a/a;");
- assertEquals("Lnaming101/a/a;", naming.lookupDescriptor(aa).toSourceString());
- DexType ba = dexItemFactory.createType("Lnaming101/b/a;");
- assertEquals("Lnaming101/b/a;", naming.lookupDescriptor(ba).toSourceString());
+ ClassSubject aa = inspector.clazz("naming101.a.a");
+ assertEquals("naming101.a.a", aa.getFinalName());
+ ClassSubject ba = inspector.clazz("naming101.b.a");
+ assertEquals("naming101.b.a", ba.getFinalName());
// Flattened to naming101, hence all other classes will be in naming101.* package.
// Due to naming101.a.a, prefix naming101.a is already used. So, any other classes,
// except for naming101.a.c, should not have naming101.a as package.
- List<String> klasses = ImmutableList.of(
- "Lnaming101/c;",
- "Lnaming101/d;",
- "Lnaming101/a/b/c;",
- "Lnaming101/b/a;",
- "Lnaming101/b/b;");
+ List<String> klasses =
+ ImmutableList.of(
+ "naming101.c", "naming101.d", "naming101.a.b.c", "naming101.b.a", "naming101.b.b");
for (String klass : klasses) {
- DexType k = dexItemFactory.createType(klass);
- String renamedName = naming.lookupDescriptor(k).toSourceString();
- assertNotEquals("naming101.a", getPackageNameFromDescriptor(renamedName));
+ ClassSubject k = inspector.clazz(klass);
+ assertNotEquals("naming101.a", k.getDexProgramClass().getType().getPackageName());
}
}
- private static void test101_rule005(DexItemFactory dexItemFactory, NamingLens naming) {
- verifyUniqueNaming(dexItemFactory, naming, CLASSES_IN_NAMING101);
+ private static void test101_rule005(CodeInspector inspector) {
+ verifyUniqueNaming(inspector, CLASSES_IN_NAMING101);
// All packages are renamed somehow. Need to check package hierarchy is consistent.
- DexType aa = dexItemFactory.createType("Lnaming101/a/a;");
- assertEquals(2, countPackageDepth(naming.lookupDescriptor(aa).toSourceString()));
- DexType abc = dexItemFactory.createType("Lnaming101/a/b/c;");
- assertEquals(3, countPackageDepth(naming.lookupDescriptor(abc).toSourceString()));
+ ClassSubject aa = inspector.clazz("naming101.a.a");
+ assertEquals(2, countPackageDepth(aa));
+ ClassSubject abc = inspector.clazz("naming101.a.b.c");
+ assertEquals(3, countPackageDepth(abc));
- // Lnaming101/a/a; -> La/a/a; --prefix--> La/a
- // Lnaming101/a/b/c; -> La/a/a/a; --prefix--> La/a/a --prefix--> La/a
+ // naming101.a/a; -> La/a/a; --prefix--> La/a
+ // naming101.a/b/c; -> La/a/a/a; --prefix--> La/a/a --prefix--> La/a
assertEquals(
- getParentPackagePrefix(naming.lookupDescriptor(aa).toSourceString()),
- getParentPackagePrefix(
- getParentPackagePrefix(naming.lookupDescriptor(abc).toSourceString())));
+ getParentPackagePrefix(aa.getFinalName()),
+ getParentPackagePrefix(getParentPackagePrefix(abc.getFinalName())));
}
- private static void test101_rule102(DexItemFactory dexItemFactory, NamingLens naming) {
- verifyUniqueNaming(dexItemFactory, naming, CLASSES_IN_NAMING101);
+ private static void test101_rule102(CodeInspector inspector) {
+ verifyUniqueNaming(inspector, CLASSES_IN_NAMING101);
// Check naming101.*.a is kept due to **.a
- DexType aa = dexItemFactory.createType("Lnaming101/a/a;");
- assertEquals("Lnaming101/a/a;", naming.lookupDescriptor(aa).toSourceString());
- DexType ba = dexItemFactory.createType("Lnaming101/b/a;");
- assertEquals("Lnaming101/b/a;", naming.lookupDescriptor(ba).toSourceString());
+ ClassSubject aa = inspector.clazz("naming101.a.a");
+ assertEquals("naming101.a.a", aa.getFinalName());
+ ClassSubject ba = inspector.clazz("naming101.b.a");
+ assertEquals("naming101.b.a", ba.getFinalName());
// Due to package-private access, classes in the same package should remain in the same package.
- DexType ac = dexItemFactory.createType("Lnaming101/a/c;");
+ ClassSubject ac = inspector.clazz("naming101.a.c");
assertEquals(
- getPackageNameFromDescriptor(naming.lookupDescriptor(aa).toString()),
- getPackageNameFromDescriptor(naming.lookupDescriptor(ac).toString()));
- DexType bb = dexItemFactory.createType("Lnaming101/b/b;");
+ aa.getDexProgramClass().getType().getPackageName(),
+ ac.getDexProgramClass().getType().getPackageName());
+ ClassSubject bb = inspector.clazz("naming101.b.b");
assertEquals(
- getPackageNameFromDescriptor(naming.lookupDescriptor(ba).toString()),
- getPackageNameFromDescriptor(naming.lookupDescriptor(bb).toString()));
+ ba.getDexProgramClass().getType().getPackageName(),
+ bb.getDexProgramClass().getType().getPackageName());
// All other classes can be repackaged to naming101.a, but naming101.a.a exists to make a name
// conflict. Thus, those should not be renamed to 'a'.
- List<String> klasses = ImmutableList.of(
- "Lnaming101/c;",
- "Lnaming101/d;",
- "Lnaming101/a/b/c;");
+ List<String> klasses = ImmutableList.of("naming101.c", "naming101.d", "naming101.a.b.c");
for (String klass : klasses) {
- DexType k = dexItemFactory.createType(klass);
- String renamedName = naming.lookupDescriptor(k).toSourceString();
- assertEquals("naming101.a", getPackageNameFromDescriptor(renamedName));
- assertNotEquals("Lnaming101/a/a;", renamedName);
+ ClassSubject k = inspector.clazz(klass);
+ assertEquals("naming101.a", k.getDexProgramClass().getType().getPackageName());
+ assertNotEquals("naming101.a.a", k.getFinalName());
}
}
- private static void test101_rule104(DexItemFactory dexItemFactory, NamingLens naming) {
- verifyUniqueNaming(dexItemFactory, naming, CLASSES_IN_NAMING101);
+ private static void test101_rule104(CodeInspector inspector) {
+ verifyUniqueNaming(inspector, CLASSES_IN_NAMING101);
// Check naming101.*.a is kept due to **.a
- DexType aa = dexItemFactory.createType("Lnaming101/a/a;");
- assertEquals("Lnaming101/a/a;", naming.lookupDescriptor(aa).toSourceString());
- DexType ba = dexItemFactory.createType("Lnaming101/b/a;");
- assertEquals("Lnaming101/b/a;", naming.lookupDescriptor(ba).toSourceString());
+ ClassSubject aa = inspector.clazz("naming101.a.a");
+ assertEquals("naming101.a.a", aa.getFinalName());
+ ClassSubject ba = inspector.clazz("naming101.b.a");
+ assertEquals("naming101.b.a", ba.getFinalName());
// Due to package-private access, classes in the same package should remain in the same package.
- DexType ac = dexItemFactory.createType("Lnaming101/a/c;");
+ ClassSubject ac = inspector.clazz("naming101.a.c");
assertEquals(
- getPackageNameFromDescriptor(naming.lookupDescriptor(aa).toString()),
- getPackageNameFromDescriptor(naming.lookupDescriptor(ac).toString()));
- DexType bb = dexItemFactory.createType("Lnaming101/b/b;");
+ aa.getDexProgramClass().getType().getPackageName(),
+ ac.getDexProgramClass().getType().getPackageName());
+ ClassSubject bb = inspector.clazz("naming101.b.b");
assertEquals(
- getPackageNameFromDescriptor(naming.lookupDescriptor(ba).toString()),
- getPackageNameFromDescriptor(naming.lookupDescriptor(bb).toString()));
+ ba.getDexProgramClass().getType().getPackageName(),
+ bb.getDexProgramClass().getType().getPackageName());
// All other packages are flattened to naming101, hence all other classes will be in
// naming101.* package. Due to naming101.a.a, prefix naming101.a is already used. So,
// remaining classes should not have naming101.a as package.
- List<String> klasses = ImmutableList.of(
- "Lnaming101/c;",
- "Lnaming101/d;",
- "Lnaming101/a/b/c;");
+ List<String> klasses = ImmutableList.of("naming101.c", "naming101.d", "naming101.a.b.c");
for (String klass : klasses) {
- DexType k = dexItemFactory.createType(klass);
- String renamedName = naming.lookupDescriptor(k).toSourceString();
- assertNotEquals("naming101.a", getPackageNameFromDescriptor(renamedName));
+ ClassSubject k = inspector.clazz(klass);
+ assertNotEquals("naming101.a", k.getDexProgramClass().getType().getPackageName());
}
}
- private static void test101_rule106(DexItemFactory dexItemFactory, NamingLens naming) {
+ private static void test101_rule106(CodeInspector inspector) {
// Classes that end with either c or d will be kept and renamed.
List<String> klasses =
CLASSES_IN_NAMING101.stream()
- .filter(className -> className.endsWith("c;") || className.endsWith("d;"))
+ .filter(className -> className.endsWith("c") || className.endsWith("d"))
.collect(Collectors.toList());
- verifyUniqueNaming(dexItemFactory, naming, klasses);
+ verifyUniqueNaming(inspector, klasses);
// Check naming101.c is kept as-is.
- DexType topC = dexItemFactory.createType("Lnaming101/c;");
- assertEquals("Lnaming101/c;", naming.lookupDescriptor(topC).toSourceString());
+ ClassSubject topC = inspector.clazz("naming101.c");
+ assertEquals("naming101.c", topC.getFinalName());
// naming101.d accesses to a package-private, static field in naming101.c.
// Therefore, it should remain in the same package.
- DexType topD = dexItemFactory.createType("Lnaming101/d;");
- String renamedTopD = naming.lookupDescriptor(topD).toSourceString();
- assertEquals("naming101", getPackageNameFromDescriptor(renamedTopD));
+ ClassSubject topD = inspector.clazz("naming101.d");
+ assertEquals("naming101", topD.getDexProgramClass().getType().getPackageName());
// Due to the keep rule for naming101.c, package prefix naming101 is reserved.
// That is, every renamed class should have naming101 as parent package prefix.
for (String klass : klasses) {
- DexType t = dexItemFactory.createType(klass);
- String rename = naming.lookupDescriptor(t).toSourceString();
- assertTrue(rename.startsWith("Lnaming101/"));
+ ClassSubject t = inspector.clazz(klass);
+ assertTrue(t.getFinalName().startsWith("naming101."));
}
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java b/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java
index 01a95ba..7511e28 100644
--- a/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/signature/SignatureOfMergedClassesTest.java
@@ -58,7 +58,7 @@
L.class,
ImplL.class)
.addKeepMainRule(Main.class)
- .addKeepClassRules(InterfaceToKeep.class)
+ .addKeepClassRules(InterfaceToKeep.class, ImplI.class, K.class)
.addKeepAttributes("Signature, InnerClasses, EnclosingMethod, *Annotation*")
.setMinApi(parameters.getApiLevel())
.noMinification()
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageInnerAndOuterClassTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageInnerAndOuterClassTest.java
new file mode 100644
index 0000000..47a1dcd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageInnerAndOuterClassTest.java
@@ -0,0 +1,87 @@
+// 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.repackage;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoStaticClassMerging;
+import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.repackage.RepackageInnerAndOuterClassTest.Outer.Inner;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageInnerAndOuterClassTest extends RepackageTestBase {
+
+ public RepackageInnerAndOuterClassTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ test(
+ testForR8(parameters.getBackend())
+ // TODO(b/169750465): It should not be necessary to explicitly remove the inner class
+ // attributes from TestClass to allow repackaging of Inner and Outer.
+ .addProgramClassFileData(transformer(TestClass.class).removeInnerClasses().transform()),
+ true);
+ }
+
+ @Test
+ public void testR8Compat() throws Exception {
+ test(testForR8Compat(parameters.getBackend()).addProgramClasses(TestClass.class), false);
+ }
+
+ private void test(R8TestBuilder<?> testBuilder, boolean eligibleForRepackaging) throws Exception {
+ testBuilder
+ .addProgramClasses(Outer.class, Inner.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepAttributeInnerClassesAndEnclosingMethod()
+ .apply(this::configureRepackaging)
+ .enableInliningAnnotations()
+ .enableNoStaticClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(inspector -> inspect(inspector, eligibleForRepackaging))
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector, boolean eligibleForRepackaging) {
+ assertThat(Outer.class, isRepackagedIf(inspector, eligibleForRepackaging));
+ assertThat(Inner.class, isRepackagedIf(inspector, eligibleForRepackaging));
+ }
+
+ public static class TestClass {
+
+ public static void main(String[] args) {
+ Outer.greet();
+ Inner.greet();
+ }
+ }
+
+ @NoStaticClassMerging
+ public static class Outer {
+
+ @NeverInline
+ public static void greet() {
+ System.out.print("Hello");
+ }
+
+ @NoStaticClassMerging
+ public static class Inner {
+
+ @NeverInline
+ public static void greet() {
+ System.out.println(" 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 2f6574b..b9d3c40 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageTest.java
@@ -169,16 +169,18 @@
// 3.A, 3.B, 3.C) Accessing a kept method is OK.
markShouldAlwaysBeEligible.accept(AccessPublicKeptMethodOnReachableClass.class);
- markShouldAlwaysBeEligible.accept(AccessPackagePrivateKeptMethodOnReachableClassDirect.class);
- markShouldAlwaysBeEligible.accept(AccessPackagePrivateKeptMethodOnReachableClassIndirect.class);
+ markEligibleWithAllowAccessModification.accept(
+ AccessPackagePrivateKeptMethodOnReachableClassDirect.class);
+ markEligibleWithAllowAccessModification.accept(
+ AccessPackagePrivateKeptMethodOnReachableClassIndirect.class);
// 4) -keepclassmembers,allowobfuscation class ReachableClassWithKeptMethod { <methods>; }
// 4.A, 4.B, 4.C) Accessing a kept method is OK.
markShouldAlwaysBeEligible.accept(AccessPublicKeptMethodAllowRenamingOnReachableClass.class);
- markShouldAlwaysBeEligible.accept(
+ markEligibleWithAllowAccessModification.accept(
AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassDirect.class);
- markShouldAlwaysBeEligible.accept(
+ markEligibleWithAllowAccessModification.accept(
AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassIndirect.class);
// 5) No keep rule.
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 8eb3d77..3e2f695 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageTestBase.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageTestBase.java
@@ -8,7 +8,7 @@
import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
import static org.junit.Assert.assertFalse;
-import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.StringUtils;
@@ -62,6 +62,10 @@
return isRepackagedAsExpected(inspector, null, eligibleForRepackaging);
}
+ protected Matcher<Class<?>> isNotRepackaged(CodeInspector inspector) {
+ return isRepackagedAsExpected(inspector, null, false);
+ }
+
/**
* Checks that the class of interest is repackaged as expected.
*
@@ -135,7 +139,7 @@
};
}
- protected void configureRepackaging(R8FullTestBuilder testBuilder) {
+ protected void configureRepackaging(R8TestBuilder<?> testBuilder) {
testBuilder
.addKeepRules(
"-" + flattenPackageHierarchyOrRepackageClasses + " \"" + getRepackagePackage() + "\"")
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithArrayMethodTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithArrayMethodTest.java
new file mode 100644
index 0000000..0216990
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithArrayMethodTest.java
@@ -0,0 +1,54 @@
+// 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.repackage;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageWithArrayMethodTest extends RepackageTestBase {
+
+ public RepackageWithArrayMethodTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .apply(this::configureRepackaging)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector) {}
+
+ public static class TestClass {
+
+ public static void main(String[] args) {
+ A[] array = new A[1];
+ array[0] = new A();
+ array.clone()[0].greet();
+ }
+ }
+
+ public static class A {
+
+ @NeverInline
+ public void greet() {
+ System.out.println("Hello world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithBridgeHoistingTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithBridgeHoistingTest.java
new file mode 100644
index 0000000..3db9028
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithBridgeHoistingTest.java
@@ -0,0 +1,134 @@
+// 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.repackage;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageWithBridgeHoistingTest extends RepackageTestBase {
+
+ public RepackageWithBridgeHoistingTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(
+ TestClass.class, GreeterInterface.class, GreeterBase.class, Greeting.class)
+ .addProgramClassFileData(
+ transformer(PrintGreeter.class)
+ .setBridge(PrintGreeter.class.getDeclaredMethod("greetBridge", Greeting.class))
+ .transform(),
+ transformer(PrintlnGreeter.class)
+ .setBridge(PrintlnGreeter.class.getDeclaredMethod("greetBridge", Greeting.class))
+ .transform())
+ .addKeepMainRule(TestClass.class)
+ .apply(this::configureRepackaging)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject greeterBase = inspector.clazz(GreeterBase.class);
+ assertThat(greeterBase, isPresent());
+ assertThat(greeterBase.uniqueMethodWithName("greetBridge"), isPresent());
+ }
+
+ public static class TestClass {
+
+ public static void main(String[] args) {
+ new PrintGreeter().greetBridge(new Greeting("Hello"));
+ new PrintGreeter().greetBridge(new Greeting(" world!"));
+
+ // Pass in an unknown greeter to prevent devirtualization in println().
+ println(System.currentTimeMillis() >= 0 ? new PrintlnGreeter() : new PrintGreeter());
+ }
+
+ @NeverInline
+ static void println(GreeterInterface greeter) {
+ greeter.greet(new Greeting(""));
+ }
+ }
+
+ @NoVerticalClassMerging
+ public interface GreeterInterface {
+
+ void greet(Greeting greeting);
+ }
+
+ public abstract static class GreeterBase implements GreeterInterface {
+
+ // Will become the holder for greetBridge() after bridge hoisting. The bridge implementation
+ // will be "invoke-virtual GreeterBase.greet()". This signature is a new non-rebound method
+ // signature introduced by bridge hoisting.
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ public static class PrintGreeter extends GreeterBase {
+
+ // Will be hoisted to GreeterBase.greetBridge().
+ @NeverInline
+ public /*bridge*/ void greetBridge(Greeting greeting) {
+ greet(greeting);
+ }
+
+ @NeverInline
+ public void greet(Greeting greeting) {
+ System.out.print(greeting);
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ public static class PrintlnGreeter extends GreeterBase {
+
+ // Will be hoisted to GreeterBase.greetBridge().
+ @NeverInline
+ public /*bridge*/ void greetBridge(Greeting greeting) {
+ greet(greeting);
+ }
+
+ @NeverInline
+ public void greet(Greeting greeting) {
+ System.out.println(greeting);
+ }
+ }
+
+ public static class Greeting {
+
+ private final String greeting;
+
+ public Greeting(String greeting) {
+ this.greeting = greeting;
+ }
+
+ @Override
+ public String toString() {
+ return greeting;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java
index 9bc8115..e548bcf 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithInitClassTest.java
@@ -4,23 +4,43 @@
package com.android.tools.r8.repackage;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class RepackageWithInitClassTest extends RepackageTestBase {
+ private final boolean enableMemberValuePropagationAnnotations;
+
+ @Parameters(name = "{2}, kind: {1}, @NeverPropagateValue: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(),
+ ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
+ getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
public RepackageWithInitClassTest(
- String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ boolean enableMemberValuePropagationAnnotations,
+ String flattenPackageHierarchyOrRepackageClasses,
+ TestParameters parameters) {
super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ this.enableMemberValuePropagationAnnotations = enableMemberValuePropagationAnnotations;
}
@Test
@@ -30,6 +50,7 @@
.addClassObfuscationDictionary("a")
.addKeepMainRule(TestClass.class)
.apply(this::configureRepackaging)
+ .enableMemberValuePropagationAnnotations(enableMemberValuePropagationAnnotations)
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
@@ -41,28 +62,45 @@
ClassSubject repackagedClassSubject = inspector.clazz(StaticMemberValuePropagation.class);
assertThat(repackagedClassSubject, isPresent());
- // Verify that a $r8$clinit field was synthesized.
String clinitFieldName = inspector.getFactory().objectMembers.clinitField.name.toSourceString();
- assertThat(repackagedClassSubject.uniqueFieldWithName(clinitFieldName), isPresent());
- assertThat(repackagedClassSubject.uniqueFieldWithName("GREETING"), not(isPresent()));
+ if (enableMemberValuePropagationAnnotations) {
+ // No $r8$clinit field should have been synthesized since we can use the HELLO field.
+ assertThat(repackagedClassSubject.uniqueFieldWithName(clinitFieldName), not(isPresent()));
+ assertThat(repackagedClassSubject.uniqueFieldWithName("HELLO"), isPresent());
- // Verify that the class was repackaged.
- assertThat(StaticMemberValuePropagation.class, isRepackaged(inspector));
+ // Verify that the WORLD field has been removed.
+ assertThat(repackagedClassSubject.uniqueFieldWithName("WORLD"), not(isPresent()));
+
+ // Verify that the class was not repackaged.
+ assertThat(StaticMemberValuePropagation.class, isNotRepackaged(inspector));
+ } else {
+ // Verify that a $r8$clinit field was synthesized.
+ assertThat(repackagedClassSubject.uniqueFieldWithName(clinitFieldName), isPresent());
+
+ // Verify that both fields have been removed.
+ assertThat(repackagedClassSubject.uniqueFieldWithName("HELLO"), not(isPresent()));
+ assertThat(repackagedClassSubject.uniqueFieldWithName("WORLD"), not(isPresent()));
+
+ // Verify that the class was repackaged.
+ assertThat(StaticMemberValuePropagation.class, isRepackaged(inspector));
+ }
}
static class TestClass {
public static void main(String[] args) {
- System.out.println(StaticMemberValuePropagation.GREETING);
+ System.out.println(StaticMemberValuePropagation.WORLD);
}
}
public static class StaticMemberValuePropagation {
- public static String GREETING = " world!";
+ @NeverPropagateValue static String HELLO = "Hello";
+
+ public static String WORLD = " world!";
static {
- System.out.print("Hello");
+ System.out.print(HELLO);
}
}
}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithKeepPackagePrivateTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithKeepPackagePrivateTest.java
new file mode 100644
index 0000000..8020e78
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithKeepPackagePrivateTest.java
@@ -0,0 +1,104 @@
+// 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.repackage;
+
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RepackageWithKeepPackagePrivateTest extends RepackageTestBase {
+
+ private final boolean allowAccessModification;
+
+ @Parameters(name = "{2}, kind: {1}, access modification: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(),
+ ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
+ getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ public RepackageWithKeepPackagePrivateTest(
+ boolean allowAccessModification,
+ String flattenPackageHierarchyOrRepackageClasses,
+ TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ this.allowAccessModification = allowAccessModification;
+ }
+
+ @Test
+ public void test() throws Exception {
+ R8TestCompileResult compileResult =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepRules(getKeepRules())
+ .addKeepPackageNamesRule(getClass().getPackage())
+ .allowAccessModification(allowAccessModification)
+ .apply(this::configureRepackaging)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect);
+
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class)
+ .addClasspathClasses(A.class, B.class)
+ .addKeepAllClassesRule()
+ .addApplyMapping(compileResult.getProguardMap())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addRunClasspathFiles(compileResult.writeToZip())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private List<String> getKeepRules() {
+ String modifiers = "allowobfuscation";
+ if (allowAccessModification) {
+ modifiers += ",allowaccessmodification";
+ }
+ return ImmutableList.of(
+ "-keep," + modifiers + " class " + A.class.getTypeName() + " { void greet(); }",
+ "-keep," + modifiers + " class " + B.class.getTypeName() + " { void greet(); }");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ assertThat(A.class, isRepackagedIf(inspector, allowAccessModification));
+ assertThat(B.class, isRepackagedIf(inspector, allowAccessModification));
+ }
+
+ public static class TestClass {
+
+ public static void main(String[] args) {
+ A.greet();
+ B.greet();
+ }
+ }
+
+ static class A {
+
+ public static void greet() {
+ System.out.print("Hello");
+ }
+ }
+
+ public static class B {
+
+ static void greet() {
+ System.out.println(" world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfPackagePrivateMethodsTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfPackagePrivateMethodsTest.java
new file mode 100644
index 0000000..951a2b6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfPackagePrivateMethodsTest.java
@@ -0,0 +1,96 @@
+// 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.repackage;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+
+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.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageWithOverridesOfPackagePrivateMethodsTest extends RepackageTestBase {
+
+ public RepackageWithOverridesOfPackagePrivateMethodsTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .apply(this::configureRepackaging)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ assertThat(HelloGreeter.class, isNotRepackaged(inspector));
+ assertThat(WorldGreeter.class, isNotRepackaged(inspector));
+ }
+
+ public static class TestClass {
+
+ public static void main(String[] args) {
+ greet(new HelloGreeter());
+ greet(new WorldGreeter());
+ }
+
+ @NeverInline
+ static void greet(HelloGreeterBase greeter) {
+ greeter.greet();
+ }
+
+ @NeverInline
+ static void greet(WorldGreeterBase greeter) {
+ greeter.greet();
+ }
+ }
+
+ @NoVerticalClassMerging
+ public abstract static class HelloGreeterBase {
+
+ abstract void greet();
+ }
+
+ @NeverClassInline
+ public static class HelloGreeter extends HelloGreeterBase {
+
+ @NeverInline
+ @Override
+ void greet() {
+ System.out.print("Hello");
+ }
+ }
+
+ @NoVerticalClassMerging
+ public abstract static class WorldGreeterBase {
+
+ abstract void greet();
+ }
+
+ @NeverClassInline
+ public static class WorldGreeter extends WorldGreeterBase {
+
+ @NeverInline
+ @Override
+ public void greet() {
+ System.out.println(" world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfProtectedMethodsTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfProtectedMethodsTest.java
new file mode 100644
index 0000000..7747cbd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfProtectedMethodsTest.java
@@ -0,0 +1,96 @@
+// 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.repackage;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+
+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.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageWithOverridesOfProtectedMethodsTest extends RepackageTestBase {
+
+ public RepackageWithOverridesOfProtectedMethodsTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .apply(this::configureRepackaging)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ assertThat(HelloGreeter.class, isRepackaged(inspector));
+ assertThat(WorldGreeter.class, isRepackaged(inspector));
+ }
+
+ public static class TestClass {
+
+ public static void main(String[] args) {
+ greet(new HelloGreeter());
+ greet(new WorldGreeter());
+ }
+
+ @NeverInline
+ static void greet(HelloGreeterBase greeter) {
+ greeter.greet();
+ }
+
+ @NeverInline
+ static void greet(WorldGreeterBase greeter) {
+ greeter.greet();
+ }
+ }
+
+ @NoVerticalClassMerging
+ public abstract static class HelloGreeterBase {
+
+ protected abstract void greet();
+ }
+
+ @NeverClassInline
+ public static class HelloGreeter extends HelloGreeterBase {
+
+ @NeverInline
+ @Override
+ protected void greet() {
+ System.out.print("Hello");
+ }
+ }
+
+ @NoVerticalClassMerging
+ public abstract static class WorldGreeterBase {
+
+ protected abstract void greet();
+ }
+
+ @NeverClassInline
+ public static class WorldGreeter extends WorldGreeterBase {
+
+ @NeverInline
+ @Override
+ public void greet() {
+ System.out.println(" world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithServiceLoaderTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithServiceLoaderTest.java
new file mode 100644
index 0000000..13ebc95
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithServiceLoaderTest.java
@@ -0,0 +1,126 @@
+// 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.repackage;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.DataResourceConsumerForTesting;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import java.util.List;
+import java.util.ServiceLoader;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageWithServiceLoaderTest extends RepackageTestBase {
+
+ public RepackageWithServiceLoaderTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void test() throws Exception {
+ Box<DataResourceConsumerForTesting> dataResourceConsumer = new Box<>();
+ R8TestRunResult runResult =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addDataEntryResources(
+ DataEntryResource.fromBytes(
+ StringUtils.lines(ServiceImpl.class.getTypeName()).getBytes(),
+ "META-INF/services/" + Service.class.getTypeName(),
+ Origin.unknown()))
+ .addKeepMainRule(TestClass.class)
+ .addOptionsModification(
+ options -> {
+ dataResourceConsumer.set(
+ new DataResourceConsumerForTesting(options.dataResourceConsumer));
+ options.dataResourceConsumer = dataResourceConsumer.get();
+ })
+ .apply(this::configureRepackaging)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+
+ CodeInspector inspector = runResult.inspector();
+ ClassSubject testClassSubject = inspector.clazz(TestClass.class);
+ assertThat(testClassSubject, isPresent());
+ assertTrue(
+ testClassSubject
+ .mainMethod()
+ .streamInstructions()
+ .filter(InstructionSubject::isInvokeStatic)
+ .map(InstructionSubject::getMethod)
+ .anyMatch(
+ method ->
+ method
+ .toSourceString()
+ .equals(
+ "java.util.ServiceLoader"
+ + " java.util.ServiceLoader.load(java.lang.Class)")));
+
+ ClassSubject serviceClassSubject = inspector.clazz(Service.class);
+ assertThat(serviceClassSubject, isPresent());
+
+ ClassSubject serviceImplClassSubject = inspector.clazz(ServiceImpl.class);
+ assertThat(serviceImplClassSubject, isPresent());
+
+ inspectResource(
+ dataResourceConsumer.get().get("META-INF/services/" + serviceClassSubject.getFinalName()),
+ serviceImplClassSubject);
+ }
+
+ private void inspectResource(List<String> contents, ClassSubject serviceImplClassSubject) {
+ assertNotNull(contents);
+ assertEquals(1, contents.size());
+ assertEquals(serviceImplClassSubject.getFinalName(), contents.get(0));
+ }
+
+ private void inspect(CodeInspector inspector) {
+ assertThat(Service.class, isRepackaged(inspector));
+ assertThat(ServiceImpl.class, isRepackaged(inspector));
+ }
+
+ public static class TestClass {
+
+ public static void main(String[] args) {
+ for (Service service : ServiceLoader.load(getServiceClass())) {
+ service.greet();
+ }
+ }
+
+ static Class<? extends Service> getServiceClass() {
+ return System.currentTimeMillis() > 0 ? Service.class : ServiceImpl.class;
+ }
+ }
+
+ public interface Service {
+
+ void greet();
+ }
+
+ public static class ServiceImpl implements Service {
+
+ @Override
+ public void greet() {
+ System.out.println("Hello world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceClassTest.java b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceClassTest.java
index 170116b..ee99ea1 100644
--- a/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceClassTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceClassTest.java
@@ -4,13 +4,19 @@
package com.android.tools.r8.rewrite.serviceloaders;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.DataEntryResource;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.DataResourceConsumerForTesting;
@@ -49,8 +55,10 @@
AppServices.SERVICE_DIRECTORY_NAME + Service.class.getTypeName(),
Origin.unknown()))
.addOptionsModification(o -> o.dataResourceConsumer = dataResourceConsumer)
+ .allowDiagnosticWarningMessages()
.setMinApi(parameters.getApiLevel())
.compile()
+ .inspectDiagnosticMessages(this::inspectDiagnosticMessages)
.addRunClasspathClasses(Service.class, ServiceImpl.class)
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithEmptyOutput();
@@ -59,6 +67,41 @@
dataResourceConsumer.get(AppServices.SERVICE_DIRECTORY_NAME + Service.class.getTypeName()));
}
+ @Test
+ public void testMainDex() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForMainDexListGenerator()
+ .addProgramClasses(TestClass.class)
+ .addLibraryFiles(ToolHelper.getFirstSupportedAndroidJar(parameters.getApiLevel()))
+ .addMainDexRules(keepMainProguardConfiguration(TestClass.class))
+ .addDataEntryResources(
+ DataEntryResource.fromBytes(
+ StringUtils.lines(ServiceImpl.class.getTypeName()).getBytes(),
+ AppServices.SERVICE_DIRECTORY_NAME + Service.class.getTypeName(),
+ Origin.unknown()))
+ .run()
+ .inspectDiagnosticMessages(this::inspectDiagnosticMessages);
+ }
+
+ private void inspectDiagnosticMessages(TestDiagnosticMessages inspector) {
+ inspector.assertWarningsCount(2);
+ inspector.assertAllWarningsMatch(
+ diagnosticMessage(
+ anyOf(
+ containsString(
+ "Unexpected reference to missing service class: "
+ + AppServices.SERVICE_DIRECTORY_NAME
+ + Service.class.getTypeName()
+ + "."),
+ containsString(
+ "Unexpected reference to missing service implementation class in "
+ + AppServices.SERVICE_DIRECTORY_NAME
+ + Service.class.getTypeName()
+ + ": "
+ + ServiceImpl.class.getTypeName()
+ + "."))));
+ }
+
private void inspectResource(List<String> contents) {
assertNotNull(contents);
assertEquals(1, contents.size());
diff --git a/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceImplementationClassTest.java b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceImplementationClassTest.java
index 5976b7c..9039c50 100644
--- a/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceImplementationClassTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceImplementationClassTest.java
@@ -4,7 +4,9 @@
package com.android.tools.r8.rewrite.serviceloaders;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -70,8 +72,22 @@
new DataResourceConsumerForTesting(options.dataResourceConsumer));
options.dataResourceConsumer = dataResourceConsumer.get();
})
+ .allowDiagnosticWarningMessages()
.setMinApi(parameters.getApiLevel())
- .compile();
+ .compile()
+ .inspectDiagnosticMessages(
+ inspector -> {
+ inspector.assertWarningsCount(1);
+ inspector.assertAllWarningsMatch(
+ diagnosticMessage(
+ containsString(
+ "Unexpected reference to missing service implementation class in "
+ + AppServices.SERVICE_DIRECTORY_NAME
+ + Service.class.getTypeName()
+ + ": "
+ + ServiceImpl.class.getTypeName()
+ + ".")));
+ });
CodeInspector inspector = compileResult.inspector();
ClassSubject serviceClassSubject = inspector.clazz(Service.class);
@@ -154,6 +170,7 @@
public static class ServiceImpl implements Service {
+ @Override
@NeverInline
public void greet() {
System.out.println("Hello world!");
diff --git a/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceImplementationClassWithFeatureTest.java b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceImplementationClassWithFeatureTest.java
index 896cc71..b3f120c 100644
--- a/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceImplementationClassWithFeatureTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/serviceloaders/MissingServiceImplementationClassWithFeatureTest.java
@@ -4,12 +4,16 @@
package com.android.tools.r8.rewrite.serviceloaders;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.containsString;
+
import com.android.tools.r8.DataEntryResource;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.rewrite.serviceloaders.MissingServiceImplementationClassTest.Service;
import com.android.tools.r8.utils.StringUtils;
import java.util.ServiceLoader;
import org.junit.Test;
@@ -44,8 +48,20 @@
AppServices.SERVICE_DIRECTORY_NAME + Service.class.getTypeName(),
Origin.unknown()))
.addFeatureSplit(FeatureClass.class)
+ .allowDiagnosticWarningMessages()
.setMinApi(parameters.getApiLevel())
- .compile();
+ .compile()
+ .inspectDiagnosticMessages(
+ inspector -> {
+ inspector.assertWarningsCount(1);
+ inspector.assertAllWarningsMatch(
+ diagnosticMessage(
+ containsString(
+ "Unexpected reference to missing service implementation class in "
+ + AppServices.SERVICE_DIRECTORY_NAME
+ + Service.class.getTypeName()
+ + ": MissingClass.")));
+ });
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
index 6bd40a0..184fdc9 100644
--- a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
@@ -100,7 +100,6 @@
@Test
public void testWithMembersPresentAnnotation() throws Exception {
- expectThrowsWithHorizontalClassMerging();
testForR8(Backend.CF)
.addProgramFiles(R8_JAR)
.addKeepRules("-keepclasseswithmembers class * { @" + PRESENT_ANNOTATION + " *** *(...); }")
diff --git a/src/test/java/com/android/tools/r8/shaking/innerclassattributes/MissingOuterClassTest.java b/src/test/java/com/android/tools/r8/shaking/innerclassattributes/MissingOuterClassTest.java
new file mode 100644
index 0000000..c16510e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/innerclassattributes/MissingOuterClassTest.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.innerclassattributes;
+
+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.TestBase;
+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.InnerClassAttribute;
+import com.android.tools.r8.shaking.innerclassattributes.MissingOuterClassTest.Outer.Inner;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+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 MissingOuterClassTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public MissingOuterClassTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepAllClassesRule()
+ .addKeepAttributeInnerClassesAndEnclosingMethod()
+ .compile()
+ .inspect(
+ inspector -> {
+ // Verify that the inner class attributes referring to MissingOuterClassTest have been
+ // removed.
+ ClassSubject outerClassSubject = inspector.clazz(Outer.class);
+ assertThat(outerClassSubject, isPresent());
+
+ ClassSubject innerClassSubject = inspector.clazz(Inner.class);
+ assertThat(innerClassSubject, isPresent());
+
+ List<InnerClassAttribute> outerInnerClassAttributes =
+ outerClassSubject.getDexProgramClass().getInnerClasses();
+ assertEquals(1, outerInnerClassAttributes.size());
+ inspectInnerClassAttribute(
+ outerClassSubject,
+ outerInnerClassAttributes.get(0),
+ outerClassSubject,
+ innerClassSubject);
+
+ List<InnerClassAttribute> innerInnerClassAttributes =
+ innerClassSubject.getDexProgramClass().getInnerClasses();
+ assertEquals(1, innerInnerClassAttributes.size());
+ inspectInnerClassAttribute(
+ innerClassSubject,
+ innerInnerClassAttributes.get(0),
+ outerClassSubject,
+ innerClassSubject);
+ });
+ }
+
+ private void inspectInnerClassAttribute(
+ ClassSubject hostClassSubject,
+ InnerClassAttribute innerClassAttribute,
+ ClassSubject outerClassSubject,
+ ClassSubject innerClassSubject) {
+ assertEquals(innerClassSubject.getDexProgramClass().getType(), innerClassAttribute.getInner());
+ assertEquals(outerClassSubject.getDexProgramClass().getType(), innerClassAttribute.getOuter());
+ if (parameters.isCfRuntime() || hostClassSubject == innerClassSubject) {
+ assertEquals(
+ DescriptorUtils.getInnerClassName(
+ outerClassSubject.getFinalDescriptor(), innerClassSubject.getFinalDescriptor()),
+ innerClassAttribute.getInnerName().toSourceString());
+ } else {
+ assertEquals(DexItemFactory.unknownTypeName, innerClassAttribute.getInnerName());
+ }
+ }
+
+ static class Outer {
+
+ static class Inner {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
index 489d8b2..88ec578 100644
--- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
@@ -14,7 +14,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.tracereferences.TraceReferencesCommandParser.OutputFormat;
+import com.android.tools.r8.tracereferences.TraceReferencesFormattingConsumer.OutputFormat;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.FileUtils;
@@ -26,6 +26,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
+import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -53,8 +54,7 @@
assertEquals(0, command.getLibrary().size());
assertEquals(0, command.getTarget().size());
assertEquals(0, command.getSource().size());
- assertEquals(TraceReferencesCommandParser.OutputFormat.PRINTUSAGE, command.getOutputFormat());
- assertNull(command.getOutput());
+ assertNull(command.getConsumer());
}
@Test(expected = CompilationFailedException.class)
@@ -105,29 +105,48 @@
}
private String formatName(OutputFormat format) {
- if (format == TraceReferencesCommandParser.OutputFormat.PRINTUSAGE) {
+ if (format == TraceReferencesFormattingConsumer.OutputFormat.PRINTUSAGE) {
return "printuses";
}
- if (format == TraceReferencesCommandParser.OutputFormat.KEEP_RULES) {
+ if (format == TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES) {
return "keep";
}
- assertSame(format, TraceReferencesCommandParser.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION);
+ assertSame(
+ format, TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION);
return "keepallowobfuscation";
}
public void runAndCheckOutput(
Path targetJar, Path sourceJar, OutputFormat format, String expected) throws Throwable {
+ runAndCheckOutput(targetJar, sourceJar, format, expected, null);
+ }
+
+ public void runAndCheckOutput(
+ Path targetJar,
+ Path sourceJar,
+ OutputFormat format,
+ String expected,
+ Consumer<DiagnosticsChecker> diagnosticsCheckerConsumer)
+ throws Throwable {
Path dir = temp.newFolder().toPath();
Path output = dir.resolve("output.txt");
+ DiagnosticsChecker diagnosticsChecker = new DiagnosticsChecker();
+ TraceReferencesFormattingConsumer consumer = new TraceReferencesFormattingConsumer(format);
TraceReferences.run(
- TraceReferencesCommand.builder()
+ TraceReferencesCommand.builder(diagnosticsChecker)
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
.addTargetFiles(targetJar)
.addSourceFiles(sourceJar)
- .setOutputPath(output)
- .setOutputFormat(format)
+ .setConsumer(consumer)
.build());
- assertEquals(expected, FileUtils.readTextFile(output, Charsets.UTF_8));
+ assertEquals(expected, consumer.get());
+ if (diagnosticsCheckerConsumer != null) {
+ diagnosticsCheckerConsumer.accept(diagnosticsChecker);
+ } else {
+ assertEquals(0, diagnosticsChecker.errors.size());
+ assertEquals(0, diagnosticsChecker.warnings.size());
+ assertEquals(0, diagnosticsChecker.infos.size());
+ }
TraceReferences.run(
TraceReferencesCommand.parse(
@@ -172,7 +191,7 @@
runAndCheckOutput(
ImmutableList.of(Target.class),
ImmutableList.of(Source.class),
- TraceReferencesCommandParser.OutputFormat.PRINTUSAGE,
+ TraceReferencesFormattingConsumer.OutputFormat.PRINTUSAGE,
StringUtils.lines(
ImmutableList.of(
"com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target",
@@ -187,7 +206,7 @@
runAndCheckOutput(
ImmutableList.of(Target.class),
ImmutableList.of(Source.class),
- TraceReferencesCommandParser.OutputFormat.KEEP_RULES,
+ TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES,
StringUtils.lines(
ImmutableList.of(
"-keep class com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target"
@@ -203,7 +222,7 @@
runAndCheckOutput(
ImmutableList.of(Target.class),
ImmutableList.of(Source.class),
- TraceReferencesCommandParser.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION,
+ TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION,
StringUtils.lines(
ImmutableList.of(
"-keep,allowobfuscation class"
@@ -219,7 +238,7 @@
runAndCheckOutput(
ImmutableList.of(OtherTarget.class),
ImmutableList.of(Source.class),
- TraceReferencesCommandParser.OutputFormat.PRINTUSAGE,
+ TraceReferencesFormattingConsumer.OutputFormat.PRINTUSAGE,
StringUtils.lines(ImmutableList.of()));
}
@@ -228,7 +247,7 @@
runAndCheckOutput(
ImmutableList.of(OtherTarget.class),
ImmutableList.of(Source.class),
- TraceReferencesCommandParser.OutputFormat.KEEP_RULES,
+ TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES,
StringUtils.lines(ImmutableList.of()));
}
@@ -237,7 +256,7 @@
runAndCheckOutput(
ImmutableList.of(OtherTarget.class),
ImmutableList.of(Source.class),
- TraceReferencesCommandParser.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION,
+ TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION,
StringUtils.lines(ImmutableList.of()));
}
@@ -264,7 +283,7 @@
runAndCheckOutput(
targetJar,
sourceJar,
- TraceReferencesCommandParser.OutputFormat.PRINTUSAGE,
+ TraceReferencesFormattingConsumer.OutputFormat.PRINTUSAGE,
StringUtils.lines(
ImmutableList.of(
"com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target",
@@ -273,7 +292,12 @@
+ ".target(int)",
"# Error: Could not find definition for field int"
+ " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target"
- + ".field")));
+ + ".field")),
+ diagnosticsChecker -> {
+ assertEquals(0, diagnosticsChecker.errors.size());
+ assertEquals(2, diagnosticsChecker.warnings.size());
+ assertEquals(0, diagnosticsChecker.infos.size());
+ });
}
@Test
@@ -289,7 +313,7 @@
runAndCheckOutput(
targetJar,
sourceJar,
- TraceReferencesCommandParser.OutputFormat.KEEP_RULES,
+ TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES,
StringUtils.lines(
ImmutableList.of(
"-keep class com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target"
@@ -301,7 +325,12 @@
+ " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target"
+ ".field",
"}",
- "-keeppackagenames com.android.tools.r8.tracereferences")));
+ "-keeppackagenames com.android.tools.r8.tracereferences")),
+ diagnosticsChecker -> {
+ assertEquals(0, diagnosticsChecker.errors.size());
+ assertEquals(2, diagnosticsChecker.warnings.size());
+ assertEquals(0, diagnosticsChecker.infos.size());
+ });
}
@Test
@@ -317,7 +346,7 @@
runAndCheckOutput(
targetJar,
sourceJar,
- TraceReferencesCommandParser.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION,
+ TraceReferencesFormattingConsumer.OutputFormat.KEEP_RULES_WITH_ALLOWOBFUSCATION,
StringUtils.lines(
ImmutableList.of(
"-keep,allowobfuscation class"
@@ -327,7 +356,12 @@
"# Error: Could not find definition for field int"
+ " com.android.tools.r8.tracereferences.TraceReferencesCommandTest$Target.field",
"}",
- "-keeppackagenames com.android.tools.r8.tracereferences")));
+ "-keeppackagenames com.android.tools.r8.tracereferences")),
+ diagnosticsChecker -> {
+ assertEquals(0, diagnosticsChecker.errors.size());
+ assertEquals(2, diagnosticsChecker.warnings.size());
+ assertEquals(0, diagnosticsChecker.infos.size());
+ });
}
private byte[] getClassWithTargetRemoved() throws IOException {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 6a5b949..efaf58b 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -245,12 +245,10 @@
}
public String getOriginalSignatureAttribute(
- DexAnnotationSet annotations, BiConsumer<GenericSignatureParser, String> parse) {
- String finalSignature = getFinalSignatureAttribute(annotations);
+ String finalSignature, BiConsumer<GenericSignatureParser, String> parse) {
if (finalSignature == null || mapping == null) {
return finalSignature;
}
-
GenericSignatureGenerator rewriter = new GenericSignatureGenerator();
GenericSignatureParser<String> parser = new GenericSignatureParser<>(rewriter);
parse.accept(parser, finalSignature);
@@ -486,6 +484,6 @@
}
public RetraceApi retrace() {
- return Retracer.create(mapping);
+ return Retracer.create(mapping == null ? ClassNameMapper.builder().build() : mapping);
}
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FieldSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FieldSubject.java
index e63a9fd..2fd8d37 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FieldSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FieldSubject.java
@@ -14,7 +14,7 @@
public abstract DexEncodedField getField();
- public DexField getFieldReference() {
+ public DexField getDexField() {
return getField().field;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index ab1d03b..3192f8b 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -28,14 +28,21 @@
import com.android.tools.r8.naming.MemberNaming.Signature;
import com.android.tools.r8.naming.signature.GenericSignatureParser;
import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.retrace.RetraceApi;
+import com.android.tools.r8.retrace.RetraceTypeResult;
+import com.android.tools.r8.retrace.RetracedField;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ZipUtils;
+import com.google.common.collect.Sets;
import java.io.File;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
import java.util.function.Consumer;
import kotlinx.metadata.jvm.KotlinClassMetadata;
import org.junit.rules.TemporaryFolder;
@@ -206,14 +213,58 @@
@Override
public FieldSubject uniqueFieldWithName(String name) {
- FieldSubject fieldSubject = null;
+ return uniqueFieldWithName(name, null);
+ }
+
+ // TODO(b/169882658): This should be removed when we have identity mappings for ambiguous cases.
+ public FieldSubject uniqueFieldWithName(String name, TypeReference originalType) {
+ RetraceApi retracer = codeInspector.retrace();
+ Set<FoundFieldSubject> candidates = Sets.newIdentityHashSet();
+ Set<FoundFieldSubject> sameTypeCandidates = Sets.newIdentityHashSet();
for (FoundFieldSubject candidate : allFields()) {
- if (candidate.getOriginalName(false).equals(name)) {
- assert fieldSubject == null;
- fieldSubject = candidate;
+ FieldReference fieldReference = candidate.getDexField().asFieldReference();
+ // TODO(b/169882658): This if should be removed completely.
+ if (candidate.getFinalName().equals(name)) {
+ candidates.add(candidate);
+ if (isNullOrEqual(originalType, fieldReference.getFieldType())) {
+ sameTypeCandidates.add(candidate);
+ }
}
+ retracer
+ .retrace(fieldReference)
+ .forEach(
+ element -> {
+ RetracedField field = element.getField();
+ if (!element.isUnknown() && field.getFieldName().equals(name)) {
+ candidates.add(candidate);
+ // TODO(b/169953605): There should not be a need for mapping the final type.
+ TypeReference fieldOriginalType = originalType;
+ if (fieldOriginalType == null) {
+ RetraceTypeResult retraceTypeResult =
+ retracer.retrace(fieldReference.getFieldType());
+ assert !retraceTypeResult.isAmbiguous();
+ fieldOriginalType =
+ retraceTypeResult.stream().iterator().next().getType().getTypeReference();
+ }
+ if (isNullOrEqual(fieldOriginalType, field.asKnown().getFieldType())) {
+ sameTypeCandidates.add(candidate);
+ }
+ }
+ });
}
- return fieldSubject != null ? fieldSubject : new AbsentFieldSubject();
+ assert candidates.size() >= sameTypeCandidates.size();
+ // If we have any merged types we cannot rely on sameTypeCandidates, so we look in all
+ // candidates first.
+ if (candidates.size() == 1) {
+ return candidates.iterator().next();
+ }
+ return sameTypeCandidates.size() == 1
+ ? sameTypeCandidates.iterator().next()
+ : new AbsentFieldSubject();
+ }
+
+ private boolean isNullOrEqual(TypeReference original, TypeReference rewritten) {
+ return original == null || original.equals(rewritten);
}
@Override
@@ -359,12 +410,12 @@
@Override
public String getOriginalSignatureAttribute() {
return codeInspector.getOriginalSignatureAttribute(
- dexClass.annotations(), GenericSignatureParser::parseClassSignature);
+ dexClass.getClassSignature().toString(), GenericSignatureParser::parseClassSignature);
}
@Override
public String getFinalSignatureAttribute() {
- return codeInspector.getFinalSignatureAttribute(dexClass.annotations());
+ return dexClass.getClassSignature().toString();
}
@Override
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 411dad2..9ef7938 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,7 +134,8 @@
@Override
public String getOriginalSignatureAttribute() {
return codeInspector.getOriginalSignatureAttribute(
- dexField.annotations(), GenericSignatureParser::parseFieldSignature);
+ codeInspector.getFinalSignatureAttribute(dexField.annotations()),
+ GenericSignatureParser::parseFieldSignature);
}
@Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index 536aa5e3..4e49582 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -186,7 +186,8 @@
@Override
public String getOriginalSignatureAttribute() {
return codeInspector.getOriginalSignatureAttribute(
- dexMethod.annotations(), GenericSignatureParser::parseMethodSignature);
+ codeInspector.getFinalSignatureAttribute(dexMethod.annotations()),
+ GenericSignatureParser::parseMethodSignature);
}
public DexMethod getOriginalDexMethod(DexItemFactory dexItemFactory) {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
index 934c06f..017a637 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.AccessFlags;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.naming.retrace.StackTrace;
import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
import com.android.tools.r8.references.MethodReference;
@@ -436,6 +437,52 @@
};
}
+ public static Matcher<FieldSubject> isFieldOfType(DexType type) {
+ return new TypeSafeMatcher<FieldSubject>() {
+ @Override
+ protected boolean matchesSafely(FieldSubject fieldSubject) {
+ return fieldSubject.getDexField().type == type;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("is field of type");
+ }
+
+ @Override
+ protected void describeMismatchSafely(FieldSubject item, Description mismatchDescription) {
+ mismatchDescription
+ .appendText(item.getOriginalSignature().toString())
+ .appendText(" is not of type ")
+ .appendText(type.toSourceString());
+ }
+ };
+ }
+
+ public static Matcher<FieldSubject> isFieldOfArrayType(
+ CodeInspector codeInspector, DexType type) {
+ return new TypeSafeMatcher<FieldSubject>() {
+ @Override
+ protected boolean matchesSafely(FieldSubject fieldSubject) {
+ return fieldSubject.getDexField().type.isArrayType()
+ && fieldSubject.getDexField().type.toBaseType(codeInspector.getFactory()) == type;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("is field of type");
+ }
+
+ @Override
+ protected void describeMismatchSafely(FieldSubject item, Description mismatchDescription) {
+ mismatchDescription
+ .appendText(item.getOriginalSignature().toString())
+ .appendText(" is not an array of type ")
+ .appendText(type.toSourceString());
+ }
+ };
+ }
+
public static Matcher<RetraceMethodResult> isInlineFrame() {
return new TypeSafeMatcher<RetraceMethodResult>() {
@Override
diff --git a/tools/r8_release.py b/tools/r8_release.py
index a048c5c..ce56269 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -15,7 +15,7 @@
import utils
-R8_DEV_BRANCH = '2.2'
+R8_DEV_BRANCH = '2.3'
R8_VERSION_FILE = os.path.join(
'src', 'main', 'java', 'com', 'android', 'tools', 'r8', 'Version.java')
THIS_FILE_RELATIVE = os.path.join('tools', 'r8_release.py')
diff --git a/tools/test.py b/tools/test.py
index c949fb3..bb8dbd2 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -112,6 +112,10 @@
result.add_option('--java-max-memory-size', '--java_max_memory_size',
help='Set memory for running tests, default 4G',
default='4G')
+ result.add_option('--test-namespace', '--test_namespace',
+ help='Only run tests in this namespace. The namespace is relative to '
+ 'com/android/tools/r8, e.g., desugar/desugaredlibrary',
+ default=None)
result.add_option('--shard-count', '--shard_count',
help='We are running this many shards.')
result.add_option('--shard-number', '--shard_number',
@@ -193,6 +197,8 @@
gradle_args.append('-Pjctf')
if options.only_jctf:
gradle_args.append('-Ponly_jctf')
+ if options.test_namespace:
+ gradle_args.append('-Ptest_namespace=%s' % options.test_namespace)
if options.jctf_compile_only:
gradle_args.append('-Pjctf_compile_only')
if options.disable_assertions: