Merge commit '987f83c71114164cc92f7c7ecbd827c8acdefdd5' into dev-release
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 8a88d3d..fa5a935 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -264,6 +264,7 @@
appView.getSyntheticItems().computeFinalSynthetics(appView);
if (result != null) {
appView.setAppInfo(new AppInfo(result.commit, appView.appInfo().getMainDexClasses()));
+ appView.pruneItems(result.prunedItems);
}
new CfApplicationWriter(
appView,
@@ -307,6 +308,7 @@
appView.getSyntheticItems().computeFinalSynthetics(appView);
if (result != null) {
appView.setAppInfo(new AppInfo(result.commit, appView.appInfo().getMainDexClasses()));
+ appView.pruneItems(result.prunedItems);
}
new ApplicationWriter(
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index 332b864..a06be47 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -131,6 +131,7 @@
appView.getSyntheticItems().computeFinalSynthetics(appView);
if (result != null) {
appView.setAppInfo(new AppInfo(result.commit, appView.appInfo().getMainDexClasses()));
+ appView.pruneItems(result.prunedItems);
}
NamingLens namingLens = PrefixRewritingNamingLens.createPrefixRewritingNamingLens(appView);
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 089c747..ea95988 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -428,8 +428,8 @@
appViewWithLiveness, appViewWithLiveness.appInfo().computeSubtypingInfo())
.run();
- if (appView.options().protoShrinking().isProtoEnumShrinkingEnabled()) {
- appView.protoShrinker().enumProtoShrinker.clearDeadEnumLiteMaps();
+ if (appView.options().protoShrinking().isEnumLiteProtoShrinkingEnabled()) {
+ appView.protoShrinker().enumLiteProtoShrinker.clearDeadEnumLiteMaps();
}
AnnotationRemover annotationRemover =
@@ -795,8 +795,8 @@
}
if (appView.options().protoShrinking().isProtoShrinkingEnabled()) {
- if (appView.options().protoShrinking().isProtoEnumShrinkingEnabled()) {
- appView.protoShrinker().enumProtoShrinker.verifyDeadEnumLiteMapsAreDead();
+ if (appView.options().protoShrinking().isEnumLiteProtoShrinkingEnabled()) {
+ appView.protoShrinker().enumLiteProtoShrinker.verifyDeadEnumLiteMapsAreDead();
}
IRConverter converter = new IRConverter(appView, timing, null, mainDexTracingResult);
@@ -849,6 +849,7 @@
lens, appBuilder.build(), memberRebindingLens.getPrevious());
}
}
+ assert Repackaging.verifyIdentityRepackaging(appView);
// Add automatic main dex classes to an eventual manual list of classes.
if (!options.mainDexKeepRules.isEmpty()) {
@@ -860,12 +861,11 @@
if (result != null) {
if (appView.appInfo().hasLiveness()) {
appViewWithLiveness.setAppInfo(
- appViewWithLiveness
- .appInfo()
- .rebuildWithLiveness(result.commit, result.removedSyntheticClasses));
+ appViewWithLiveness.appInfo().rebuildWithLiveness(result.commit));
} else {
appView.setAppInfo(appView.appInfo().rebuildWithClassHierarchy(result.commit));
}
+ appViewWithLiveness.pruneItems(result.prunedItems);
}
// Perform minification.
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index 3c5f4b7..cfacdbb 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -63,6 +63,18 @@
this.obsolete = obsolete;
}
+ public AppInfo prunedCopyFrom(PrunedItems prunedItems) {
+ assert getClass() == AppInfo.class;
+ assert checkIfObsolete();
+ assert prunedItems.getPrunedApp() == app();
+ if (prunedItems.isEmpty()) {
+ return this;
+ }
+ return new AppInfo(
+ getSyntheticItems().commitPrunedItems(prunedItems),
+ getMainDexClasses().withoutPrunedItems(prunedItems));
+ }
+
protected InternalOptions options() {
return app.options;
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
index 144b1de..b6100c2 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -93,6 +93,20 @@
getMainDexClasses());
}
+ @Override
+ public AppInfoWithClassHierarchy prunedCopyFrom(PrunedItems prunedItems) {
+ assert getClass() == AppInfoWithClassHierarchy.class;
+ assert checkIfObsolete();
+ assert prunedItems.getPrunedApp() == app();
+ if (prunedItems.isEmpty()) {
+ return this;
+ }
+ return new AppInfoWithClassHierarchy(
+ getSyntheticItems().commitPrunedItems(prunedItems),
+ getClassToFeatureSplitMap().withoutPrunedItems(prunedItems),
+ getMainDexClasses().withoutPrunedItems(prunedItems));
+ }
+
public ClassToFeatureSplitMap getClassToFeatureSplitMap() {
return classToFeatureSplitMap;
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 8e0800b..566fa31 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -90,7 +90,7 @@
// TODO(b/169115389): Remove
private Set<DexMethod> cfByteCodePassThrough = ImmutableSet.of();
- private Map<DexClass, DexValueString> sourceDebugExtensions = new IdentityHashMap<>();
+ private Map<DexType, DexValueString> sourceDebugExtensions = new IdentityHashMap<>();
// When input has been (partially) desugared these are the classes which has been library
// desugared. This information is populated in the IR converter.
@@ -251,11 +251,11 @@
}
public void setSourceDebugExtensionForType(DexClass clazz, DexValueString sourceDebugExtension) {
- this.sourceDebugExtensions.put(clazz, sourceDebugExtension);
+ sourceDebugExtensions.put(clazz.type, sourceDebugExtension);
}
public DexValueString getSourceDebugExtensionForType(DexClass clazz) {
- return this.sourceDebugExtensions.get(clazz);
+ return sourceDebugExtensions.get(clazz.type);
}
@Override
@@ -321,8 +321,8 @@
}
public <U> U withProtoEnumShrinker(Function<EnumLiteProtoShrinker, U> fn, U defaultValue) {
- if (protoShrinker != null && options().protoShrinking().isProtoEnumShrinkingEnabled()) {
- return fn.apply(protoShrinker.enumProtoShrinker);
+ if (protoShrinker != null && options().protoShrinking().isEnumLiteProtoShrinkingEnabled()) {
+ return fn.apply(protoShrinker.enumLiteProtoShrinker);
}
return defaultValue;
}
@@ -569,16 +569,27 @@
}
public void pruneItems(PrunedItems prunedItems) {
- pruneItems(prunedItems, withLiveness());
- }
-
- private static void pruneItems(PrunedItems prunedItems, AppView<AppInfoWithLiveness> appView) {
- if (!prunedItems.hasRemovedClasses() && !appView.options().configurationDebugging) {
- assert appView.appInfo().app() == prunedItems.getPrunedApp();
+ if (prunedItems.isEmpty()) {
+ assert appInfo().app() == prunedItems.getPrunedApp();
return;
}
- appView.setAppInfo(appView.appInfo().prunedCopyFrom(prunedItems));
- appView.setAppServices(appView.appServices().prunedCopy(prunedItems));
+ if (appInfo.hasLiveness()) {
+ AppView<AppInfoWithLiveness> self = withLiveness();
+ self.setAppInfo(self.appInfo().prunedCopyFrom(prunedItems));
+ } else if (appInfo.hasClassHierarchy()) {
+ AppView<AppInfoWithClassHierarchy> self = withClassHierarchy();
+ self.setAppInfo(self.appInfo().prunedCopyFrom(prunedItems));
+ } else {
+ pruneAppInfo(prunedItems, this);
+ }
+ if (appServices() != null) {
+ setAppServices(appServices().prunedCopy(prunedItems));
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void pruneAppInfo(PrunedItems prunedItems, AppView<?> appView) {
+ ((AppView<AppInfo>) appView).setAppInfo(appView.appInfo().prunedCopyFrom(prunedItems));
}
public void rewriteWithLens(NonIdentityGraphLens lens) {
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 0faef1b..1ace989 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -46,9 +46,6 @@
public static final DexProgramClass[] EMPTY_ARRAY = {};
- private static final DexEncodedArray SENTINEL_NOT_YET_COMPUTED =
- new DexEncodedArray(DexValue.EMPTY_ARRAY);
-
private final ProgramResource.Kind originKind;
private final Collection<DexProgramClass> synthesizedFrom;
private CfVersion initialClassFileVersion = null;
diff --git a/src/main/java/com/android/tools/r8/graph/DexValue.java b/src/main/java/com/android/tools/r8/graph/DexValue.java
index 97d304b..f355030 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -307,6 +307,10 @@
return null;
}
+ public boolean isNestedDexValue() {
+ return false;
+ }
+
public abstract AbstractValue toAbstractValue(AbstractValueFactory factory);
static DexValue fromAsmBootstrapArgument(
@@ -1133,6 +1137,11 @@
}
@Override
+ public boolean isNestedDexValue() {
+ return true;
+ }
+
+ @Override
public DexType getType(DexItemFactory factory) {
throw new Unreachable();
}
diff --git a/src/main/java/com/android/tools/r8/graph/PrunedItems.java b/src/main/java/com/android/tools/r8/graph/PrunedItems.java
index 2df596c..ed42622 100644
--- a/src/main/java/com/android/tools/r8/graph/PrunedItems.java
+++ b/src/main/java/com/android/tools/r8/graph/PrunedItems.java
@@ -34,6 +34,10 @@
return new Builder().setPrunedApp(application).build();
}
+ public boolean isEmpty() {
+ return removedClasses.isEmpty() && additionalPinnedItems.isEmpty();
+ }
+
public DexApplication getPrunedApp() {
return prunedApp;
}
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java b/src/main/java/com/android/tools/r8/graph/TreeFixerBase.java
similarity index 71%
rename from src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java
rename to src/main/java/com/android/tools/r8/graph/TreeFixerBase.java
index 17d06a3..a10db7147 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/graph/TreeFixerBase.java
@@ -2,64 +2,67 @@
// 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.repackaging;
+package com.android.tools.r8.graph;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-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.DirectMappedDexApplication;
-import com.android.tools.r8.graph.EnclosingMethodAttribute;
-import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.graph.NestHostClassAttribute;
-import com.android.tools.r8.graph.NestMemberClassAttribute;
-import com.android.tools.r8.shaking.AnnotationFixer;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.ArrayList;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
-public class RepackagingTreeFixer {
+public abstract class TreeFixerBase {
- private final DirectMappedDexApplication.Builder appBuilder;
- private final AppView<AppInfoWithLiveness> appView;
+ private final AppView<?> appView;
private final DexItemFactory dexItemFactory;
- private final RepackagingLens.Builder lensBuilder = new RepackagingLens.Builder();
- private final Map<DexType, DexType> repackagedClasses;
- private final Map<DexType, DexProgramClass> newProgramClasses = new IdentityHashMap<>();
+ private Map<DexType, DexProgramClass> newProgramClasses = null;
private final Map<DexType, DexProgramClass> synthesizedFromClasses = new IdentityHashMap<>();
private final Map<DexProto, DexProto> protoFixupCache = new IdentityHashMap<>();
- public RepackagingTreeFixer(
- DirectMappedDexApplication.Builder appBuilder,
- AppView<AppInfoWithLiveness> appView,
- Map<DexType, DexType> repackagedClasses) {
- this.appBuilder = appBuilder;
+ public TreeFixerBase(AppView<?> appView) {
this.appView = appView;
this.dexItemFactory = appView.dexItemFactory();
- this.repackagedClasses = repackagedClasses;
}
- public RepackagingLens run() {
- // Globally substitute repackaged class types.
- for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) {
+ /** Mapping of a class type to a potentially new class type. */
+ public abstract DexType mapClassType(DexType type);
+
+ /** Callback invoked each time an encoded field changes field reference. */
+ public abstract void recordFieldChange(DexField from, DexField to);
+
+ /** Callback invoked each time an encoded method changes method reference. */
+ public abstract void recordMethodChange(DexMethod from, DexMethod to);
+
+ /** Callback invoked each time a program class definition changes type reference. */
+ public abstract void recordClassChange(DexType from, DexType to);
+
+ private DexProgramClass recordClassChange(DexProgramClass from, DexProgramClass to) {
+ recordClassChange(from.getType(), to.getType());
+ return to;
+ }
+
+ private DexEncodedField recordFieldChange(DexEncodedField from, DexEncodedField to) {
+ recordFieldChange(from.field, to.field);
+ return to;
+ }
+
+ /** Callback to allow custom handling when an encoded method changes. */
+ public DexEncodedMethod recordMethodChange(DexEncodedMethod from, DexEncodedMethod to) {
+ recordMethodChange(from.method, to.method);
+ return to;
+ }
+
+ /** Fixup a collection of classes. */
+ public Collection<DexProgramClass> fixupClasses(Collection<DexProgramClass> classes) {
+ assert newProgramClasses == null;
+ newProgramClasses = new IdentityHashMap<>();
+ for (DexProgramClass clazz : classes) {
newProgramClasses.computeIfAbsent(clazz.getType(), ignore -> fixupClass(clazz));
}
- appBuilder.replaceProgramClasses(new ArrayList<>(newProgramClasses.values()));
- RepackagingLens lens = lensBuilder.build(appView);
- new AnnotationFixer(lens).run(newProgramClasses.values());
- return lens;
+ return newProgramClasses.values();
}
+ // Should remain private as the correctness of the fixup requires the lazy 'newProgramClasses'.
private DexProgramClass fixupClass(DexProgramClass clazz) {
DexProgramClass newClass =
new DexProgramClass(
@@ -93,8 +96,19 @@
fixupMethods(
clazz.getMethodCollection().virtualMethods(),
clazz.getMethodCollection().numberOfVirtualMethods()));
+ // Transfer properties that are not passed to the constructor.
+ if (clazz.hasClassFileVersion()) {
+ newClass.setInitialClassFileVersion(clazz.getInitialClassFileVersion());
+ }
+ if (clazz.isDeprecated()) {
+ newClass.setDeprecated();
+ }
+ if (clazz.getKotlinInfo() != null) {
+ newClass.setKotlinInfo(clazz.getKotlinInfo());
+ }
+ // If the class type changed, record the move in the lens.
if (newClass.getType() != clazz.getType()) {
- lensBuilder.recordMove(clazz.getType(), newClass.getType());
+ return recordClassChange(clazz, newClass);
}
return newClass;
}
@@ -120,7 +134,8 @@
: enclosingMethodAttribute;
}
- private DexEncodedField[] fixupFields(List<DexEncodedField> fields) {
+ /** Fixup a list of fields. */
+ public DexEncodedField[] fixupFields(List<DexEncodedField> fields) {
if (fields == null) {
return DexEncodedField.EMPTY_ARRAY;
}
@@ -135,13 +150,13 @@
DexField fieldReference = field.getReference();
DexField newFieldReference = fixupFieldReference(fieldReference);
if (newFieldReference != fieldReference) {
- lensBuilder.recordMove(fieldReference, newFieldReference);
- return field.toTypeSubstitutedField(newFieldReference);
+ return recordFieldChange(field, field.toTypeSubstitutedField(newFieldReference));
}
return field;
}
- private DexField fixupFieldReference(DexField field) {
+ /** Fixup a field reference. */
+ public DexField fixupFieldReference(DexField field) {
DexType newType = fixupType(field.type);
DexType newHolder = fixupType(field.holder);
return dexItemFactory.createField(newHolder, newType, field.name);
@@ -181,21 +196,20 @@
}
return newMethods;
}
-
- private DexEncodedMethod fixupMethod(DexEncodedMethod method) {
+ /** Fixup a method definition. */
+ public DexEncodedMethod fixupMethod(DexEncodedMethod method) {
DexMethod methodReference = method.getReference();
DexMethod newMethodReference = fixupMethodReference(methodReference);
if (newMethodReference != methodReference) {
- lensBuilder.recordMove(methodReference, newMethodReference);
- return method.toTypeSubstitutedMethod(newMethodReference);
+ return recordMethodChange(method, method.toTypeSubstitutedMethod(newMethodReference));
}
return method;
}
- private DexMethod fixupMethodReference(DexMethod method) {
- return appView
- .dexItemFactory()
- .createMethod(fixupType(method.holder), fixupProto(method.proto), method.name);
+ /** Fixup a method reference. */
+ public DexMethod fixupMethodReference(DexMethod method) {
+ return dexItemFactory.createMethod(
+ fixupType(method.holder), fixupProto(method.proto), method.name);
}
private NestHostClassAttribute fixupNestHost(NestHostClassAttribute nestHostClassAttribute) {
@@ -221,7 +235,8 @@
return changed ? newNestMemberAttributes : nestMemberAttributes;
}
- private DexProto fixupProto(DexProto proto) {
+ /** Fixup a proto descriptor. */
+ public DexProto fixupProto(DexProto proto) {
DexProto result = protoFixupCache.get(proto);
if (result == null) {
DexType returnType = fixupType(proto.returnType);
@@ -232,8 +247,10 @@
return result;
}
+ // Should remain private as its correctness relies on the setup of 'newProgramClasses'.
private Collection<DexProgramClass> fixupSynthesizedFrom(
Collection<DexProgramClass> synthesizedFrom) {
+ assert newProgramClasses != null;
if (synthesizedFrom.isEmpty()) {
return synthesizedFrom;
}
@@ -258,7 +275,8 @@
return type != null ? fixupType(type) : null;
}
- private DexType fixupType(DexType type) {
+ /** Fixup a type reference. */
+ public DexType fixupType(DexType type) {
if (type.isArrayType()) {
DexType base = type.toBaseType(dexItemFactory);
DexType fixed = fixupType(base);
@@ -268,7 +286,7 @@
return type.replaceBaseType(fixed, dexItemFactory);
}
if (type.isClassType()) {
- return repackagedClasses.getOrDefault(type, type);
+ return mapClassType(type);
}
return type;
}
@@ -289,4 +307,9 @@
DexType[] newTypes = fixupTypes(types.values);
return newTypes != types.values ? new DexTypeList(newTypes) : types;
}
+
+ /** Fixup a method signature. */
+ public DexMethodSignature fixupMethodSignature(DexMethodSignature signature) {
+ return signature.withProto(fixupProto(signature.getProto()));
+ }
}
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 0f85054..87a1e31 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.horizontalclassmerging;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass.FieldSetter;
import com.android.tools.r8.graph.DexEncodedField;
@@ -16,6 +17,7 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.TreeFixerBase;
import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
import com.android.tools.r8.shaking.AnnotationFixer;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -38,7 +40,7 @@
* have been remapped to new classes by the class merger. While doing so, all updated changes are
* tracked in {@link TreeFixer#lensBuilder}.
*/
-class TreeFixer {
+class TreeFixer extends TreeFixerBase {
private final Map<DexProto, DexProto> protoFixupCache = new IdentityHashMap<>();
private final HorizontallyMergedClasses mergedClasses;
private final HorizontalClassMergerGraphLens.Builder lensBuilder;
@@ -55,6 +57,7 @@
HorizontalClassMergerGraphLens.Builder lensBuilder,
FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder,
SyntheticArgumentClass syntheticArgumentClass) {
+ super(appView);
this.appView = appView;
this.mergedClasses = mergedClasses;
this.lensBuilder = lensBuilder;
@@ -384,53 +387,26 @@
}
}
- public DexField fixupFieldReference(DexField field) {
- DexType newType = fixupType(field.type);
- DexType newHolder = fixupType(field.holder);
- return appView.dexItemFactory().createField(newHolder, newType, field.name);
+ @Override
+ public DexType mapClassType(DexType type) {
+ return mergedClasses.getMergeTargetOrDefault(type);
}
- private DexMethod fixupMethodReference(DexMethod method) {
- return appView
- .dexItemFactory()
- .createMethod(fixupType(method.holder), fixupProto(method.proto), method.name);
+ @Override
+ public void recordClassChange(DexType from, DexType to) {
+ // Classes are not changed but in-place updated.
+ throw new Unreachable();
}
- private DexMethodSignature fixupMethodSignature(DexMethodSignature signature) {
- return signature.withProto(fixupProto(signature.getProto()));
+ @Override
+ public void recordFieldChange(DexField from, DexField to) {
+ // Fields are manually changed in 'fixupFields' above.
+ throw new Unreachable();
}
- private DexProto fixupProto(DexProto proto) {
- DexProto result = protoFixupCache.get(proto);
- if (result == null) {
- DexType returnType = fixupType(proto.returnType);
- DexType[] arguments = fixupTypes(proto.parameters.values);
- result = appView.dexItemFactory().createProto(returnType, arguments);
- protoFixupCache.put(proto, result);
- }
- return result;
- }
-
- private DexType fixupType(DexType type) {
- if (type.isArrayType()) {
- DexType base = type.toBaseType(appView.dexItemFactory());
- DexType fixed = fixupType(base);
- if (base == fixed) {
- return type;
- }
- return type.replaceBaseType(fixed, appView.dexItemFactory());
- }
- if (type.isClassType()) {
- type = mergedClasses.getMergeTargetOrDefault(type);
- }
- return type;
- }
-
- private DexType[] fixupTypes(DexType[] types) {
- DexType[] result = new DexType[types.length];
- for (int i = 0; i < result.length; i++) {
- result[i] = fixupType(types[i]);
- }
- return result;
+ @Override
+ public void recordMethodChange(DexMethod from, DexMethod to) {
+ // Methods are manually changed in 'fixupMethods' above.
+ throw new Unreachable();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
index 03455fb..281215f 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -316,9 +316,11 @@
return computeObjectState(definition.outValue());
}
if (definition.isStaticGet()) {
- // TODO(b/166532388) : Enums with many instance rely on staticGets to set the $VALUES data
- // instead of directly keeping the values in registers. We could consider analysing these
- // and answer the analysed object state here.
+ // Enums with many instance rely on staticGets to set the $VALUES data instead of directly
+ // keeping the values in registers, due to the max capacity of the redundant field load
+ // elimination. The capacity has already been increased, so that this case is extremely
+ // uncommon (very large enums).
+ // TODO(b/169050248): We could consider analysing these to answer the object state here.
return ObjectState.empty();
}
return ObjectState.empty();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/EnumLiteProtoShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/EnumLiteProtoShrinker.java
index 0b04f9b..64f5bea 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/EnumLiteProtoShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/EnumLiteProtoShrinker.java
@@ -56,7 +56,7 @@
}
public void clearDeadEnumLiteMaps() {
- assert appView.options().protoShrinking().isProtoEnumShrinkingEnabled();
+ assert appView.options().protoShrinking().isEnumLiteProtoShrinkingEnabled();
// The optimization only enables further enums to be unboxed, no point to run it if enum
// unboxing is disabled.
if (!appView.options().enableEnumUnboxing) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnumSwitchMapRemover.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnumSwitchMapRemover.java
new file mode 100644
index 0000000..00e824f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnumSwitchMapRemover.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.analysis.proto;
+
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues;
+import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues.EnumStaticFieldValues;
+import com.android.tools.r8.ir.analysis.value.ObjectState;
+import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/** Proto enum switch maps can be removed even if the static enum fields are pinned. */
+public class ProtoEnumSwitchMapRemover {
+
+ private final ProtoReferences references;
+
+ private final Map<DexType, EnumStaticFieldValues> staticFieldValuesMap =
+ new ConcurrentHashMap<>();
+
+ public ProtoEnumSwitchMapRemover(ProtoReferences references) {
+ this.references = references;
+ }
+
+ public void recordStaticValues(DexProgramClass clazz, StaticFieldValues staticFieldValues) {
+ if (staticFieldValues == null || !staticFieldValues.isEnumStaticFieldValues()) {
+ return;
+ }
+ assert clazz.isEnum();
+ EnumStaticFieldValues enumStaticFieldValues = staticFieldValues.asEnumStaticFieldValues();
+ if (isProtoEnum(clazz)) {
+ staticFieldValuesMap.put(clazz.type, enumStaticFieldValues);
+ }
+ }
+
+ private boolean isProtoEnum(DexProgramClass clazz) {
+ assert clazz.isEnum();
+ if (clazz.type == references.methodToInvokeType) {
+ return true;
+ }
+ return clazz.getInterfaces().contains(references.enumLiteMapType);
+ }
+
+ public SingleNumberValue getOrdinal(
+ DexProgramClass enumClass, DexEncodedField enumInstanceField, DexEncodedField ordinalField) {
+ if (enumClass == null || !isProtoEnum(enumClass)) {
+ return null;
+ }
+ EnumStaticFieldValues enumStaticFieldValues = staticFieldValuesMap.get(enumClass.type);
+ if (enumStaticFieldValues == null) {
+ if (enumClass.type == references.methodToInvokeType) {
+ throw new CompilationError("Proto optimizations: missing information for MethodToInvoke.");
+ }
+ return null;
+ }
+ ObjectState state =
+ enumStaticFieldValues.getObjectStateForPossiblyPinnedField(enumInstanceField.field);
+ if (state == null) {
+ return null;
+ }
+ return state.getAbstractFieldValue(ordinalField).asSingleNumberValue();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java
index 2674265..a0f75d7 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java
@@ -20,7 +20,8 @@
public final GeneratedExtensionRegistryShrinker generatedExtensionRegistryShrinker;
public final GeneratedMessageLiteShrinker generatedMessageLiteShrinker;
public final GeneratedMessageLiteBuilderShrinker generatedMessageLiteBuilderShrinker;
- public final EnumLiteProtoShrinker enumProtoShrinker;
+ public final EnumLiteProtoShrinker enumLiteProtoShrinker;
+ public final ProtoEnumSwitchMapRemover protoEnumSwitchMapRemover;
public final ProtoReferences references;
private Set<DexType> deadProtoTypes = Sets.newIdentityHashSet();
@@ -42,10 +43,14 @@
appView.options().protoShrinking().enableGeneratedMessageLiteBuilderShrinking
? new GeneratedMessageLiteBuilderShrinker(appView, references)
: null;
- this.enumProtoShrinker =
- appView.options().protoShrinking().isProtoEnumShrinkingEnabled()
+ this.enumLiteProtoShrinker =
+ appView.options().protoShrinking().isEnumLiteProtoShrinkingEnabled()
? new EnumLiteProtoShrinker(appView, references)
: null;
+ this.protoEnumSwitchMapRemover =
+ appView.options().protoShrinking().enableRemoveProtoEnumSwitchMap()
+ ? new ProtoEnumSwitchMapRemover(references)
+ : null;
this.references = references;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java
index 363a2de..c3ab146 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.ir.analysis.proto.schema.ProtoFieldType;
import com.android.tools.r8.ir.analysis.proto.schema.ProtoFieldTypeFactory;
import com.android.tools.r8.ir.analysis.proto.schema.ProtoMessageInfo;
+import com.android.tools.r8.ir.analysis.proto.schema.ProtoMessageInfo.ProtoMessageInfoBuilderException;
import com.android.tools.r8.ir.analysis.proto.schema.ProtoObject;
import com.android.tools.r8.ir.analysis.proto.schema.ProtoObjectFromInvokeStatic;
import com.android.tools.r8.ir.analysis.proto.schema.ProtoObjectFromStaticGet;
@@ -184,7 +185,7 @@
}
return builder.build();
- } catch (InvalidRawMessageInfoException e) {
+ } catch (InvalidRawMessageInfoException | ProtoMessageInfoBuilderException e) {
// This should generally not happen, so leave an assert here just in case.
assert false;
return null;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java
index 28de572..6135fb5 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoMessageInfo.java
@@ -59,7 +59,7 @@
oneOfObjects.add(new ProtoOneOfObjectPair(oneOfObject, oneOfCaseObject));
}
- public ProtoMessageInfo build() {
+ public ProtoMessageInfo build() throws ProtoMessageInfoBuilderException {
removeDeadFields();
removeUnusedSharedData();
return new ProtoMessageInfo(dynamicMethod, flags, fields, hasBitsObjects, oneOfObjects);
@@ -79,7 +79,7 @@
}
}
- private void removeUnusedSharedData() {
+ private void removeUnusedSharedData() throws ProtoMessageInfoBuilderException {
if (fields == null || fields.isEmpty()) {
oneOfObjects = null;
hasBitsObjects = null;
@@ -149,6 +149,13 @@
i++;
}
+ // We should not have any bits pointing to dead proto fields.
+ for (ProtoFieldObject hasBitsObject : hasBitsObjects) {
+ if (hasBitsObject.isDeadProtoFieldObject()) {
+ throw new ProtoMessageInfoBuilderException();
+ }
+ }
+
assert hasBitsObjects.stream().noneMatch(ProtoFieldObject::isDeadProtoFieldObject);
}
@@ -171,6 +178,10 @@
}
}
+ public static class ProtoMessageInfoBuilderException extends Exception {
+ private ProtoMessageInfoBuilderException() {}
+ }
+
private final ProgramMethod dynamicMethod;
private final int flags;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java
index 4821b2b..f33a1d4 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeElement.java
@@ -56,6 +56,11 @@
return nesting;
}
+ @Override
+ public boolean isPrimitiveArrayType() {
+ return memberTypeLattice.isPrimitiveType();
+ }
+
public TypeElement getMemberType() {
return memberTypeLattice;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
index 10f5ece..095333f 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
@@ -262,6 +262,10 @@
return false;
}
+ public boolean isPrimitiveArrayType() {
+ return false;
+ }
+
public ArrayTypeElement asArrayType() {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 042441e..c1d51db 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -2251,6 +2251,15 @@
}
private Value getUninitializedDebugLocalValue(int register, ValueTypeConstraint typeConstraint) {
+ if (appView.options().invalidDebugInfoStrict) {
+ throw new InvalidDebugInfoException(
+ "Information in locals-table is invalid. "
+ + "Local refers to uninitialized register: "
+ + register
+ + " with constraint "
+ + typeConstraint
+ + ".");
+ }
// A debug initiated value must have a precise type constraint.
assert typeConstraint.isPrecise();
TypeElement type = typeConstraint.isObject() ? getNull() : typeConstraint.toPrimitiveType();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 7861771..a6203e9 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -1724,6 +1724,12 @@
if (enumUnboxer != null) {
enumUnboxer.recordEnumState(method.getHolder(), staticFieldValues);
}
+ if (appView.options().protoShrinking().enableRemoveProtoEnumSwitchMap()) {
+ appView
+ .protoShrinker()
+ .protoEnumSwitchMapRemover
+ .recordStaticValues(method.getHolder(), staticFieldValues);
+ }
methodOptimizationInfoCollector.collectMethodOptimizationInfo(
method, code, feedback, dynamicTypeOptimization, instanceFieldInitializationInfos, timing);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index cf605e2..76e3b0f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.graph.UseRegistry.MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY;
import static com.android.tools.r8.ir.code.Invoke.Type.STATIC;
import static com.android.tools.r8.ir.code.Invoke.Type.VIRTUAL;
+import static com.android.tools.r8.ir.code.Opcodes.ASSUME;
import static com.android.tools.r8.ir.code.Opcodes.CHECK_CAST;
import static com.android.tools.r8.ir.code.Opcodes.CONST_CLASS;
import static com.android.tools.r8.ir.code.Opcodes.CONST_METHOD_HANDLE;
@@ -56,6 +57,7 @@
import com.android.tools.r8.ir.analysis.type.DestructivePhiTypeUpdater;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
+import com.android.tools.r8.ir.code.Assume;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.CatchHandlers;
import com.android.tools.r8.ir.code.CheckCast;
@@ -595,6 +597,50 @@
}
break;
+ case ASSUME:
+ {
+ // TODO(b/174543992): It's not clear we should rewrite the assumes here. The code
+ // present fixes the problem for enum unboxing, but not for lambda merging.
+ // The LensCodeRewriter is run before most assume instructions are inserted, however,
+ // the call site optimization may propagate assumptions at IR building time, and such
+ // assumes are already present.
+ // R8 clears the assumes if the type is rewritten to a primitive type.
+ Assume assume = current.asAssume();
+ if (assume.hasOutValue()) {
+ TypeElement type = assume.getOutType();
+ TypeElement substituted =
+ type.fixupClassTypeReferences(graphLens::lookupType, appView);
+ if (substituted != type) {
+ assert type.isArrayType() || type.isClassType();
+ if (substituted.isPrimitiveType()) {
+ assert type.isClassType();
+ assert appView.unboxedEnums().isUnboxedEnum(type.asClassType().getClassType());
+ // Any assumption of a class type being converted to a primitive type is
+ // invalid. Dynamic type is irrelevant and non null is incorrect.
+ assume.outValue().replaceUsers(assume.src());
+ iterator.removeOrReplaceByDebugLocalRead();
+ } else if (substituted.isPrimitiveArrayType()) {
+ assert type.isArrayType();
+ // Non-null assumptions on a class array type being converted to a primitive
+ // array type remains, but dynamic type becomes irrelevant.
+ assume.unsetDynamicTypeAssumption();
+ if (assume.hasNonNullAssumption()) {
+ current.outValue().setType(substituted);
+ affectedPhis.addAll(current.outValue().uniquePhiUsers());
+ } else {
+ iterator.removeOrReplaceByDebugLocalRead();
+ }
+ } else {
+ assert !substituted.isPrimitiveType();
+ assert !substituted.isPrimitiveArrayType();
+ current.outValue().setType(substituted);
+ affectedPhis.addAll(current.outValue().uniquePhiUsers());
+ }
+ }
+ }
+ }
+ break;
+
default:
if (current.hasOutValue()) {
// For all other instructions, substitute any changed type.
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 d73a7c1..42787b7 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
@@ -240,7 +240,6 @@
}
MethodAccessFlags newFlags = virtual.accessFlags.copy();
- newFlags.unsetBridge();
newFlags.promoteToStatic();
DexEncodedMethod.setDebugInfoWithFakeThisParameter(
code, companionMethod.getArity(), appView);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
index cabff87..10b8b25 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
@@ -54,11 +54,12 @@
public class RedundantFieldLoadElimination {
private static final int MAX_CAPACITY = 10000;
- private static final int MAX_CAPACITY_PER_BLOCK = 50;
+ private static final int MIN_CAPACITY_PER_BLOCK = 50;
private final AppView<?> appView;
private final ProgramMethod method;
private final IRCode code;
+ private final int maxCapacityPerBlock;
// Values that may require type propagation.
private final Set<Value> affectedValues = Sets.newIdentityHashSet();
@@ -74,6 +75,7 @@
this.appView = appView;
this.method = code.context();
this.code = code;
+ this.maxCapacityPerBlock = Math.max(MIN_CAPACITY_PER_BLOCK, MAX_CAPACITY / code.blocks.size());
}
public static boolean shouldRun(AppView<?> appView, IRCode code) {
@@ -185,7 +187,7 @@
// Already visited.
continue;
}
- activeState = activeStates.computeActiveStateOnBlockEntry(head);
+ activeState = activeStates.computeActiveStateOnBlockEntry(head, maxCapacityPerBlock);
activeStates.removeDeadBlockExitStates(head, pendingNormalSuccessors);
BasicBlock block = head;
BasicBlock end = null;
@@ -444,19 +446,20 @@
private int capacity = MAX_CAPACITY;
- BlockState computeActiveStateOnBlockEntry(BasicBlock block) {
+ BlockState computeActiveStateOnBlockEntry(BasicBlock block, int maxCapacityPerBlock) {
if (block.isEntry()) {
- return new BlockState();
+ return new BlockState(maxCapacityPerBlock);
}
List<BasicBlock> predecessors = block.getPredecessors();
Iterator<BasicBlock> predecessorIterator = predecessors.iterator();
- BlockState state = new BlockState(activeStateAtExit.get(predecessorIterator.next()));
+ BlockState state =
+ new BlockState(maxCapacityPerBlock, activeStateAtExit.get(predecessorIterator.next()));
while (predecessorIterator.hasNext()) {
BasicBlock predecessor = predecessorIterator.next();
BlockState predecessorExitState = activeStateAtExit.get(predecessor);
if (predecessorExitState == null) {
// Not processed yet.
- return new BlockState();
+ return new BlockState(maxCapacityPerBlock);
}
state.intersect(predecessorExitState);
}
@@ -479,7 +482,7 @@
private void ensureCapacity(BlockState state) {
int stateSize = state.size();
- assert stateSize <= MAX_CAPACITY_PER_BLOCK;
+ assert stateSize <= state.maxCapacity;
int numberOfItemsToRemove = stateSize - capacity;
if (numberOfItemsToRemove <= 0) {
return;
@@ -568,9 +571,14 @@
private LinkedHashMap<DexField, FieldValue> nonFinalStaticFieldValues;
- public BlockState() {}
+ private final int maxCapacity;
- public BlockState(BlockState state) {
+ public BlockState(int maxCapacity) {
+ this.maxCapacity = maxCapacity;
+ }
+
+ public BlockState(int maxCapacity, BlockState state) {
+ this(maxCapacity);
if (state != null) {
if (state.finalInstanceFieldValues != null && !state.finalInstanceFieldValues.isEmpty()) {
finalInstanceFieldValues = new LinkedHashMap<>();
@@ -606,8 +614,8 @@
public void ensureCapacityForNewElement() {
int size = size();
- assert size <= MAX_CAPACITY_PER_BLOCK;
- if (size == MAX_CAPACITY_PER_BLOCK) {
+ assert size <= maxCapacity;
+ if (size == maxCapacity) {
reduceSize(1);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
index b7addaa..88eedce 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
@@ -307,6 +307,20 @@
// is null, the same NPE happens at runtime, we can assume the value is non null in the
// switch map after the ordinal call.
SingleNumberValue ordinalValue = getOrdinalValue(code, abstractValue, true);
+ if (ordinalValue == null
+ && appView.options().protoShrinking().enableRemoveProtoEnumSwitchMap()) {
+ ordinalValue =
+ appView
+ .protoShrinker()
+ .protoEnumSwitchMapRemover
+ .getOrdinal(
+ appView.programDefinitionFor(info.enumClass, code.context()),
+ enumInstanceField,
+ appView
+ .appInfo()
+ .resolveField(factory.enumMembers.ordinalField, code.context())
+ .getResolvedField());
+ }
if (ordinalValue == null) {
return null;
}
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 79b2e1e..51e419b 100644
--- a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
+++ b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
@@ -8,8 +8,11 @@
import static com.android.tools.r8.utils.DescriptorUtils.DESCRIPTOR_PACKAGE_SEPARATOR;
import static com.android.tools.r8.utils.DescriptorUtils.INNER_CLASS_SEPARATOR;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DirectMappedDexApplication;
@@ -17,14 +20,21 @@
import com.android.tools.r8.graph.ProgramPackage;
import com.android.tools.r8.graph.ProgramPackageCollection;
import com.android.tools.r8.graph.SortedProgramPackageCollection;
+import com.android.tools.r8.graph.TreeFixerBase;
+import com.android.tools.r8.repackaging.RepackagingLens.Builder;
+import com.android.tools.r8.shaking.AnnotationFixer;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.ProguardConfiguration;
+import com.android.tools.r8.synthesis.CommittedItems;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -63,6 +73,55 @@
return lens;
}
+ public static boolean verifyIdentityRepackaging(
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
+ // Running the tree fixer with an identity mapping helps ensure that the fixup of of items is
+ // complete as the rewrite replaces all items regardless of repackaging.
+ // The identity mapping should result in no move callbacks being called.
+ Collection<DexProgramClass> newProgramClasses =
+ new TreeFixerBase(appView) {
+ @Override
+ public DexType mapClassType(DexType type) {
+ return type;
+ }
+
+ @Override
+ public void recordFieldChange(DexField from, DexField to) {
+ assert false;
+ }
+
+ @Override
+ public void recordMethodChange(DexMethod from, DexMethod to) {
+ assert false;
+ }
+
+ @Override
+ public void recordClassChange(DexType from, DexType to) {
+ assert false;
+ }
+ }.fixupClasses(appView.appInfo().classesWithDeterministicOrder());
+ CommittedItems committedItems =
+ appView
+ .getSyntheticItems()
+ .commit(
+ appView
+ .appInfo()
+ .app()
+ .builder()
+ .replaceProgramClasses(new ArrayList<>(newProgramClasses))
+ .build());
+ if (appView.appInfo().hasLiveness()) {
+ appView
+ .withLiveness()
+ .setAppInfo(appView.withLiveness().appInfo().rebuildWithLiveness(committedItems));
+ } else {
+ appView
+ .withClassHierarchy()
+ .setAppInfo(appView.appInfo().rebuildWithClassHierarchy(committedItems));
+ }
+ return true;
+ }
+
private RepackagingLens run(
DirectMappedDexApplication.Builder appBuilder, ExecutorService executorService)
throws ExecutionException {
@@ -80,7 +139,48 @@
if (mappings.isEmpty()) {
return null;
}
- return new RepackagingTreeFixer(appBuilder, appView, mappings).run();
+ RepackagingLens.Builder lensBuilder = new RepackagingLens.Builder();
+ List<DexProgramClass> newProgramClasses =
+ new ArrayList<>(
+ new RepackagingTreeFixer(appView, mappings, lensBuilder)
+ .fixupClasses(appView.appInfo().classesWithDeterministicOrder()));
+ appBuilder.replaceProgramClasses(newProgramClasses);
+ RepackagingLens lens = lensBuilder.build(appView);
+ new AnnotationFixer(lens).run(appBuilder.getProgramClasses());
+ return lens;
+ }
+
+ private static class RepackagingTreeFixer extends TreeFixerBase {
+
+ private final BiMap<DexType, DexType> mappings;
+ private final Builder lensBuilder;
+
+ public RepackagingTreeFixer(
+ AppView<?> appView, BiMap<DexType, DexType> mappings, Builder lensBuilder) {
+ super(appView);
+ this.mappings = mappings;
+ this.lensBuilder = lensBuilder;
+ }
+
+ @Override
+ public DexType mapClassType(DexType type) {
+ return mappings.getOrDefault(type, type);
+ }
+
+ @Override
+ public void recordFieldChange(DexField from, DexField to) {
+ lensBuilder.recordMove(from, to);
+ }
+
+ @Override
+ public void recordMethodChange(DexMethod from, DexMethod to) {
+ lensBuilder.recordMove(from, to);
+ }
+
+ @Override
+ public void recordClassChange(DexType from, DexType to) {
+ lensBuilder.recordMove(from, to);
+ }
}
private void processPackagesInDesiredLocation(
diff --git a/src/main/java/com/android/tools/r8/retrace/Retrace.java b/src/main/java/com/android/tools/r8/retrace/Retrace.java
index 90c1cca..ef0bff7 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -15,13 +15,8 @@
import com.android.tools.r8.retrace.RetraceCommand.ProguardMapProducer;
import com.android.tools.r8.retrace.internal.PlainStackTraceVisitor;
import com.android.tools.r8.retrace.internal.RetraceAbortException;
-import com.android.tools.r8.retrace.internal.RetraceCommandLineResult;
import com.android.tools.r8.retrace.internal.RetraceRegularExpression;
-import com.android.tools.r8.retrace.internal.RetracerImpl;
-import com.android.tools.r8.retrace.internal.StackTraceElementProxyRetracerImpl;
-import com.android.tools.r8.retrace.internal.StackTraceElementProxyRetracerImpl.RetraceStackTraceProxyImpl;
import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy;
-import com.android.tools.r8.retrace.internal.StackTraceVisitor;
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.OptionsParsing;
@@ -43,6 +38,7 @@
import java.util.List;
import java.util.Map;
import java.util.Scanner;
+import java.util.function.BiConsumer;
/**
* A retrace tool for obfuscated stack traces.
@@ -170,61 +166,18 @@
try {
Timing timing = Timing.create("R8 retrace", command.printMemory());
timing.begin("Read proguard map");
- RetracerImpl retracer =
- RetracerImpl.create(command.proguardMapProducer, command.diagnosticsHandler);
+ Retracer retracer =
+ Retracer.createDefault(command.proguardMapProducer, command.diagnosticsHandler);
timing.end();
- RetraceCommandLineResult result;
timing.begin("Parse and Retrace");
StackTraceVisitor<StackTraceElementStringProxy> stackTraceVisitor =
command.regularExpression != null
- ? new RetraceRegularExpression(
- retracer, command.stackTrace, command.regularExpression)
+ ? new RetraceRegularExpression(command.stackTrace, command.regularExpression)
: new PlainStackTraceVisitor(command.stackTrace, command.diagnosticsHandler);
- StackTraceElementProxyRetracer<StackTraceElementStringProxy> proxyRetracer =
- new StackTraceElementProxyRetracerImpl<>(retracer);
- List<String> retracedStrings = new ArrayList<>();
- stackTraceVisitor.forEach(
- stackTraceElement -> {
- Box<List<RetraceStackTraceProxyImpl<StackTraceElementStringProxy>>> currentList =
- new Box<>();
- Map<
- RetraceStackTraceProxyImpl<StackTraceElementStringProxy>,
- List<RetraceStackTraceProxyImpl<StackTraceElementStringProxy>>>
- ambiguousBlocks = new HashMap<>();
- proxyRetracer
- .retrace(stackTraceElement)
- .forEach(
- retracedElement -> {
- if (retracedElement.isTopFrame() || !retracedElement.hasRetracedClass()) {
- List<RetraceStackTraceProxyImpl<StackTraceElementStringProxy>> block =
- new ArrayList<>();
- ambiguousBlocks.put(retracedElement, block);
- currentList.set(block);
- }
- currentList.get().add(retracedElement);
- });
- ambiguousBlocks.keySet().stream()
- .sorted()
- .forEach(
- topFrame -> {
- ambiguousBlocks
- .get(topFrame)
- .forEach(
- frame -> {
- StackTraceElementStringProxy originalItem = frame.getOriginalItem();
- retracedStrings.add(
- originalItem.toRetracedItem(
- frame, !currentList.isSet(), command.isVerbose));
- // Use the current list as indicator for us seeing the first
- // sorted element.
- currentList.set(null);
- });
- });
- });
- result = new RetraceCommandLineResult(retracedStrings);
timing.end();
timing.begin("Report result");
- command.retracedStackTraceConsumer.accept(result.getNodes());
+ command.retracedStackTraceConsumer.accept(
+ runInternal(stackTraceVisitor, retracer, command.isVerbose));
timing.end();
if (command.printTimes()) {
timing.report();
@@ -235,6 +188,75 @@
}
}
+ /**
+ * Entry point for running retrace with default parsing on a stack trace
+ *
+ * @param retracer the retracer used to parse each stack trace frame.
+ * @param stackTrace the stack trace to be parsed.
+ * @param isVerbose if the output should embed verbose information.
+ * @return the retraced strings in flat format
+ */
+ public static List<String> run(Retracer retracer, List<String> stackTrace, boolean isVerbose) {
+ return runInternal(
+ new RetraceRegularExpression(stackTrace, DEFAULT_REGULAR_EXPRESSION), retracer, isVerbose);
+ }
+
+ private static List<String> runInternal(
+ StackTraceVisitor<StackTraceElementStringProxy> stackTraceVisitor,
+ Retracer retracer,
+ boolean isVerbose) {
+ List<String> retracedStrings = new ArrayList<>();
+ Box<StackTraceElementStringProxy> lastReportedFrame = new Box<>();
+ run(
+ stackTraceVisitor,
+ StackTraceElementProxyRetracer.createDefault(retracer),
+ (stackTraceElement, frames) -> {
+ frames.forEach(
+ frame -> {
+ StackTraceElementStringProxy originalItem = frame.getOriginalItem();
+ retracedStrings.add(
+ originalItem.toRetracedItem(
+ frame, lastReportedFrame.get() == stackTraceElement, isVerbose));
+ lastReportedFrame.set(stackTraceElement);
+ });
+ });
+ return retracedStrings;
+ }
+
+ /**
+ * @param stackTraceVisitor the stack trace visitor.
+ * @param proxyRetracer the retracer used to parse each stack trace frame.
+ * @param resultConsumer consumer to accept each parsed stack trace frame. The stack-trace element
+ * is guaranteed to be unique per line.
+ */
+ public static <T extends StackTraceElementProxy<?>> void run(
+ StackTraceVisitor<T> stackTraceVisitor,
+ StackTraceElementProxyRetracer<T> proxyRetracer,
+ BiConsumer<T, List<RetraceStackTraceProxy<T>>> resultConsumer) {
+ stackTraceVisitor.forEach(
+ stackTraceElement -> {
+ Box<List<RetraceStackTraceProxy<T>>> currentList = new Box<>();
+ Map<RetraceStackTraceProxy<T>, List<RetraceStackTraceProxy<T>>> ambiguousBlocks =
+ new HashMap<>();
+ proxyRetracer
+ .retrace(stackTraceElement)
+ .forEach(
+ retracedElement -> {
+ if (retracedElement.isTopFrame() || !retracedElement.hasRetracedClass()) {
+ List<RetraceStackTraceProxy<T>> block = new ArrayList<>();
+ ambiguousBlocks.put(retracedElement, block);
+ currentList.set(block);
+ }
+ currentList.get().add(retracedElement);
+ });
+ ambiguousBlocks.keySet().stream()
+ .sorted()
+ .forEach(
+ topFrame ->
+ resultConsumer.accept(stackTraceElement, ambiguousBlocks.get(topFrame)));
+ });
+ }
+
public static void run(String[] args) throws RetraceFailedException {
// To be compatible with standard retrace and remapper, we translate -arg into --arg.
String[] mappedArgs = new String[args.length];
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java b/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java
index f9d0d6f..6751a2a 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceClassResult.java
@@ -33,6 +33,8 @@
RetraceClassResult forEach(Consumer<Element> resultConsumer);
+ boolean hasRetraceResult();
+
boolean isAmbiguous();
@Keep
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 b6247df..d9f67ef 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
@@ -9,6 +9,7 @@
import java.util.List;
import java.util.function.Consumer;
+@Keep
public class RetraceCommand {
final boolean isVerbose;
@@ -144,7 +145,6 @@
}
}
- @Keep
public interface ProguardMapProducer {
String get();
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceInvalidStackTraceLineDiagnostics.java b/src/main/java/com/android/tools/r8/retrace/RetraceInvalidStackTraceLineDiagnostics.java
similarity index 92%
rename from src/main/java/com/android/tools/r8/retrace/internal/RetraceInvalidStackTraceLineDiagnostics.java
rename to src/main/java/com/android/tools/r8/retrace/RetraceInvalidStackTraceLineDiagnostics.java
index 547534f..d2a6827 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceInvalidStackTraceLineDiagnostics.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceInvalidStackTraceLineDiagnostics.java
@@ -2,7 +2,7 @@
// 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.retrace.internal;
+package com.android.tools.r8.retrace;
import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.Keep;
@@ -40,7 +40,7 @@
return message;
}
- static RetraceInvalidStackTraceLineDiagnostics createNull(int lineNumber) {
+ public static RetraceInvalidStackTraceLineDiagnostics createNull(int lineNumber) {
return new RetraceInvalidStackTraceLineDiagnostics(lineNumber, NULL_STACK_TRACE_LINE_MESSAGE);
}
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 57b7607..ed1d338 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retracer.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retracer.java
@@ -4,11 +4,14 @@
package com.android.tools.r8.retrace;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.Keep;
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.TypeReference;
+import com.android.tools.r8.retrace.RetraceCommand.ProguardMapProducer;
+import com.android.tools.r8.retrace.internal.RetracerImpl;
/** This is the main api interface for retrace. */
@Keep
@@ -23,4 +26,9 @@
RetraceFieldResult retraceField(FieldReference fieldReference);
RetraceTypeResult retraceType(TypeReference typeReference);
+
+ static Retracer createDefault(
+ ProguardMapProducer proguardMapProducer, DiagnosticsHandler diagnosticsHandler) {
+ return RetracerImpl.create(proguardMapProducer, diagnosticsHandler);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxyRetracer.java b/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxyRetracer.java
index 17d9005..17de5f1 100644
--- a/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxyRetracer.java
+++ b/src/main/java/com/android/tools/r8/retrace/StackTraceElementProxyRetracer.java
@@ -5,11 +5,16 @@
package com.android.tools.r8.retrace;
import com.android.tools.r8.Keep;
-import com.android.tools.r8.retrace.internal.StackTraceElementProxyRetracerImpl.RetraceStackTraceProxyImpl;
+import com.android.tools.r8.retrace.internal.StackTraceElementProxyRetracerImpl;
import java.util.stream.Stream;
@Keep
public interface StackTraceElementProxyRetracer<T extends StackTraceElementProxy<?>> {
- Stream<RetraceStackTraceProxyImpl<T>> retrace(T element);
+ Stream<RetraceStackTraceProxy<T>> retrace(T element);
+
+ static <T extends StackTraceElementProxy<?>> StackTraceElementProxyRetracer<T> createDefault(
+ Retracer retracer) {
+ return new StackTraceElementProxyRetracerImpl<>(retracer);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceVisitor.java b/src/main/java/com/android/tools/r8/retrace/StackTraceVisitor.java
similarity index 77%
rename from src/main/java/com/android/tools/r8/retrace/internal/StackTraceVisitor.java
rename to src/main/java/com/android/tools/r8/retrace/StackTraceVisitor.java
index dfe937a..bf2de73 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceVisitor.java
+++ b/src/main/java/com/android/tools/r8/retrace/StackTraceVisitor.java
@@ -2,11 +2,12 @@
// 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.retrace.internal;
+package com.android.tools.r8.retrace;
-import com.android.tools.r8.retrace.StackTraceElementProxy;
+import com.android.tools.r8.Keep;
import java.util.function.Consumer;
+@Keep
public interface StackTraceVisitor<T extends StackTraceElementProxy<?>> {
void forEach(Consumer<T> consumer);
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceVisitor.java b/src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceVisitor.java
index 7f7d7fc..90fedbd 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceVisitor.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/PlainStackTraceVisitor.java
@@ -7,6 +7,8 @@
import static com.google.common.base.Predicates.not;
import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.retrace.RetraceInvalidStackTraceLineDiagnostics;
+import com.android.tools.r8.retrace.StackTraceVisitor;
import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy.ClassNameType;
import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy.StackTraceElementStringProxyBuilder;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -165,8 +167,8 @@
.registerClassName(classStartIndex, methodSeparator, ClassNameType.TYPENAME)
.registerMethodName(methodSeparator + 1, parensStart);
// Check if we have a filename and position.
- int separatorIndex = firstCharFromIndex(line, parensStart, ':');
- if (separatorIndex < parensEnd) {
+ int separatorIndex = line.lastIndexOf(':', parensEnd);
+ if (separatorIndex > -1 && separatorIndex < parensEnd) {
builder.registerSourceFile(parensStart + 1, separatorIndex);
builder.registerLineNumber(separatorIndex + 1, parensEnd);
} else {
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java
index 45b7fdd..15fdfdc 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.retrace.RetraceClassResult;
import com.android.tools.r8.retrace.RetraceFrameResult;
+import com.android.tools.r8.retrace.Retracer;
import com.android.tools.r8.utils.Pair;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
@@ -29,17 +30,17 @@
private final ClassReference obfuscatedReference;
private final ClassNamingForNameMapper mapper;
- private final RetracerImpl retracer;
+ private final Retracer retracer;
private RetraceClassResultImpl(
- ClassReference obfuscatedReference, ClassNamingForNameMapper mapper, RetracerImpl retracer) {
+ ClassReference obfuscatedReference, ClassNamingForNameMapper mapper, Retracer retracer) {
this.obfuscatedReference = obfuscatedReference;
this.mapper = mapper;
this.retracer = retracer;
}
static RetraceClassResultImpl create(
- ClassReference obfuscatedReference, ClassNamingForNameMapper mapper, RetracerImpl retracer) {
+ ClassReference obfuscatedReference, ClassNamingForNameMapper mapper, Retracer retracer) {
return new RetraceClassResultImpl(obfuscatedReference, mapper, retracer);
}
@@ -162,7 +163,8 @@
: mappedRangesForPosition;
}
- boolean hasRetraceResult() {
+ @Override
+ public boolean hasRetraceResult() {
return mapper != null;
}
@@ -196,7 +198,7 @@
RetraceClassResultImpl classResult,
List<Pair<ElementImpl, T>> mappings,
D definition,
- RetracerImpl retracer);
+ Retracer retracer);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
index 3a65b0a..73b7dbd 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
@@ -4,31 +4,30 @@
package com.android.tools.r8.retrace.internal;
-import com.android.tools.r8.Keep;
import com.android.tools.r8.naming.MemberNaming;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.retrace.RetraceFieldResult;
import com.android.tools.r8.retrace.RetraceSourceFileResult;
+import com.android.tools.r8.retrace.Retracer;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.Pair;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Stream;
-@Keep
public class RetraceFieldResultImpl implements RetraceFieldResult {
private final RetraceClassResultImpl classResult;
private final List<Pair<RetraceClassResultImpl.ElementImpl, List<MemberNaming>>> memberNamings;
private final FieldDefinition fieldDefinition;
- private final RetracerImpl retracer;
+ private final Retracer retracer;
RetraceFieldResultImpl(
RetraceClassResultImpl classResult,
List<Pair<RetraceClassResultImpl.ElementImpl, List<MemberNaming>>> memberNamings,
FieldDefinition fieldDefinition,
- RetracerImpl retracer) {
+ Retracer retracer) {
this.classResult = classResult;
this.memberNamings = memberNamings;
this.fieldDefinition = fieldDefinition;
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
index 24c5a8e..35f1f63 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.retrace.RetraceSourceFileResult;
import com.android.tools.r8.retrace.RetracedClassMember;
import com.android.tools.r8.retrace.RetracedMethod;
+import com.android.tools.r8.retrace.Retracer;
import com.android.tools.r8.utils.Pair;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
@@ -29,14 +30,14 @@
private final MethodDefinition methodDefinition;
private final int obfuscatedPosition;
private final List<Pair<RetraceClassResultImpl.ElementImpl, List<MappedRange>>> mappedRanges;
- private final RetracerImpl retracer;
+ private final Retracer retracer;
public RetraceFrameResultImpl(
RetraceClassResultImpl classResult,
List<Pair<RetraceClassResultImpl.ElementImpl, List<MappedRange>>> mappedRanges,
MethodDefinition methodDefinition,
int obfuscatedPosition,
- RetracerImpl retracer) {
+ Retracer retracer) {
this.classResult = classResult;
this.methodDefinition = methodDefinition;
this.obfuscatedPosition = obfuscatedPosition;
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
index 93fa544..46d63bc 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
@@ -4,11 +4,11 @@
package com.android.tools.r8.retrace.internal;
-import com.android.tools.r8.Keep;
import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRangesOfName;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.retrace.RetraceMethodResult;
+import com.android.tools.r8.retrace.Retracer;
import com.android.tools.r8.utils.Pair;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
@@ -16,19 +16,18 @@
import java.util.function.Consumer;
import java.util.stream.Stream;
-@Keep
public class RetraceMethodResultImpl implements RetraceMethodResult {
private final MethodDefinition methodDefinition;
private final RetraceClassResultImpl classResult;
private final List<Pair<RetraceClassResultImpl.ElementImpl, List<MappedRange>>> mappedRanges;
- private final RetracerImpl retracer;
+ private final Retracer retracer;
RetraceMethodResultImpl(
RetraceClassResultImpl classResult,
List<Pair<RetraceClassResultImpl.ElementImpl, List<MappedRange>>> mappedRanges,
MethodDefinition methodDefinition,
- RetracerImpl retracer) {
+ Retracer retracer) {
this.classResult = classResult;
this.mappedRanges = mappedRanges;
this.methodDefinition = methodDefinition;
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceRegularExpression.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceRegularExpression.java
index ca9a2f8..d475594 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceRegularExpression.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceRegularExpression.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.retrace.internal;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.retrace.StackTraceVisitor;
import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy.ClassNameType;
import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy.StackTraceElementStringProxyBuilder;
import java.util.ArrayList;
@@ -15,7 +16,6 @@
public class RetraceRegularExpression implements StackTraceVisitor<StackTraceElementStringProxy> {
- private final RetracerImpl retracer;
private final List<String> stackTrace;
private final String regularExpression;
@@ -35,9 +35,7 @@
private static final String CAPTURE_GROUP_PREFIX = "captureGroup";
private static final int FIRST_CAPTURE_GROUP_INDEX = 0;
- public RetraceRegularExpression(
- RetracerImpl retracer, List<String> stackTrace, String regularExpression) {
- this.retracer = retracer;
+ public RetraceRegularExpression(List<String> stackTrace, String regularExpression) {
this.stackTrace = stackTrace;
this.regularExpression = regularExpression;
}
@@ -168,6 +166,8 @@
}
}
+ private static final String anyLetterWithMarkers = "\\p{L}\\p{M}*+";
+
// TODO(b/145731185): Extend support for identifiers with strings inside back ticks.
private static final String javaIdentifierSegment =
"\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*";
@@ -275,7 +275,7 @@
@Override
String subExpression() {
- return "(?:(\\w*[\\. ])?(\\w*)?)";
+ return "(?:([" + anyLetterWithMarkers + "_: \\.]*[" + anyLetterWithMarkers + "_\\.])?)";
}
@Override
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceTypeResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceTypeResultImpl.java
index 0680800..b48d5f3 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceTypeResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceTypeResultImpl.java
@@ -6,20 +6,21 @@
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.retrace.RetraceTypeResult;
+import com.android.tools.r8.retrace.Retracer;
import java.util.function.Consumer;
import java.util.stream.Stream;
public class RetraceTypeResultImpl implements RetraceTypeResult {
private final TypeReference obfuscatedType;
- private final RetracerImpl retracer;
+ private final Retracer retracer;
- private RetraceTypeResultImpl(TypeReference obfuscatedType, RetracerImpl retracer) {
+ private RetraceTypeResultImpl(TypeReference obfuscatedType, Retracer retracer) {
this.obfuscatedType = obfuscatedType;
this.retracer = retracer;
}
- static RetraceTypeResultImpl create(TypeReference obfuscatedType, RetracerImpl retracer) {
+ static RetraceTypeResultImpl create(TypeReference obfuscatedType, Retracer retracer) {
return new RetraceTypeResultImpl(obfuscatedType, retracer);
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceUtils.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceUtils.java
index 46e8ddc..dc96687 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceUtils.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceUtils.java
@@ -10,11 +10,13 @@
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.retrace.RetraceClassResult;
import com.android.tools.r8.retrace.RetraceClassResult.Element;
import com.android.tools.r8.retrace.RetraceSourceFileResult;
import com.android.tools.r8.retrace.RetracedClass;
import com.android.tools.r8.retrace.RetracedMethod;
import com.android.tools.r8.retrace.RetracedMethod.KnownRetracedMethod;
+import com.android.tools.r8.retrace.Retracer;
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.DescriptorUtils;
import com.google.common.collect.Sets;
@@ -72,7 +74,7 @@
}
static RetraceSourceFileResult getSourceFile(
- Element classElement, RetracedClass context, String sourceFile, RetracerImpl retracer) {
+ Element classElement, RetracedClass context, String sourceFile, Retracer retracer) {
// If no context is specified always retrace using the found class element.
if (context == null) {
return classElement.retraceSourceFile(sourceFile);
@@ -80,8 +82,7 @@
if (context.equals(classElement.getRetracedClass())) {
return classElement.retraceSourceFile(sourceFile);
} else {
- RetraceClassResultImpl contextClassResult =
- retracer.retraceClass(context.getClassReference());
+ RetraceClassResult contextClassResult = retracer.retraceClass(context.getClassReference());
assert !contextClassResult.isAmbiguous();
if (contextClassResult.hasRetraceResult()) {
Box<RetraceSourceFileResult> retraceSourceFile = new Box<>();
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetracedClassImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetracedClassImpl.java
index b2da5d8..47c41b9 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetracedClassImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetracedClassImpl.java
@@ -4,11 +4,9 @@
package com.android.tools.r8.retrace.internal;
-import com.android.tools.r8.Keep;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.retrace.RetracedClass;
-@Keep
public final class RetracedClassImpl implements RetracedClass {
private final ClassReference classReference;
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetracedFieldImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetracedFieldImpl.java
index aee0cde..9dd96c8 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetracedFieldImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetracedFieldImpl.java
@@ -4,13 +4,11 @@
package com.android.tools.r8.retrace.internal;
-import com.android.tools.r8.Keep;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.retrace.RetracedField;
import java.util.Objects;
-@Keep
public abstract class RetracedFieldImpl implements RetracedField {
private RetracedFieldImpl() {}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetracedMethodImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetracedMethodImpl.java
index 1f4ad68..ec4ff42 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetracedMethodImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetracedMethodImpl.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.retrace.internal;
-import com.android.tools.r8.Keep;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.retrace.RetracedMethod;
@@ -14,7 +13,6 @@
import java.util.Objects;
import java.util.Optional;
-@Keep
public abstract class RetracedMethodImpl implements RetracedMethod {
private static final int NO_POSITION = -1;
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetracedTypeImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetracedTypeImpl.java
index d467cc1..38d1a83 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetracedTypeImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetracedTypeImpl.java
@@ -4,13 +4,11 @@
package com.android.tools.r8.retrace.internal;
-import com.android.tools.r8.Keep;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.retrace.RetracedType;
import java.util.Objects;
-@Keep
public final class RetracedTypeImpl implements RetracedType {
private final TypeReference typeReference;
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
index a949fc1..c3453b5 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
@@ -8,11 +8,14 @@
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.retrace.RetraceClassResult;
import com.android.tools.r8.retrace.RetraceFieldResult;
+import com.android.tools.r8.retrace.RetraceFrameResult;
import com.android.tools.r8.retrace.RetraceStackTraceProxy;
+import com.android.tools.r8.retrace.RetraceTypeResult;
import com.android.tools.r8.retrace.RetracedClass;
import com.android.tools.r8.retrace.RetracedField;
import com.android.tools.r8.retrace.RetracedMethod;
import com.android.tools.r8.retrace.RetracedType;
+import com.android.tools.r8.retrace.Retracer;
import com.android.tools.r8.retrace.StackTraceElementProxy;
import com.android.tools.r8.retrace.StackTraceElementProxyRetracer;
import com.android.tools.r8.utils.Box;
@@ -27,18 +30,18 @@
public class StackTraceElementProxyRetracerImpl<T extends StackTraceElementProxy<?>>
implements StackTraceElementProxyRetracer<T> {
- private final RetracerImpl retracer;
+ private final Retracer retracer;
- public StackTraceElementProxyRetracerImpl(RetracerImpl retracer) {
+ public StackTraceElementProxyRetracerImpl(Retracer retracer) {
this.retracer = retracer;
}
@Override
- public Stream<RetraceStackTraceProxyImpl<T>> retrace(T element) {
+ public Stream<RetraceStackTraceProxy<T>> retrace(T element) {
if (!element.hasClassName()) {
return Stream.of(RetraceStackTraceProxyImpl.builder(element).build());
}
- RetraceClassResultImpl classResult = retracer.retraceClass(element.getClassReference());
+ RetraceClassResult classResult = retracer.retraceClass(element.getClassReference());
if (element.hasMethodName()) {
return retraceMethod(element, classResult);
} else if (element.hasFieldName()) {
@@ -48,7 +51,7 @@
}
}
- private Stream<RetraceStackTraceProxyImpl<T>> retraceClassOrType(
+ private Stream<RetraceStackTraceProxy<T>> retraceClassOrType(
T element, RetraceClassResult classResult) {
return classResult.stream()
.flatMap(
@@ -76,15 +79,15 @@
})));
}
- private Stream<RetraceStackTraceProxyImpl<T>> retraceMethod(
- T element, RetraceClassResultImpl classResult) {
+ private Stream<RetraceStackTraceProxy<T>> retraceMethod(
+ T element, RetraceClassResult classResult) {
return retraceFieldOrReturnType(element)
.flatMap(
fieldOrReturnTypeConsumer ->
retracedMethodArguments(element)
.flatMap(
argumentsConsumer -> {
- RetraceFrameResultImpl frameResult =
+ RetraceFrameResult frameResult =
element.hasLineNumber()
? classResult.lookupFrame(
element.getMethodName(), element.getLineNumber())
@@ -123,7 +126,7 @@
}));
}
- private Stream<RetraceStackTraceProxyImpl<T>> retraceField(
+ private Stream<RetraceStackTraceProxy<T>> retraceField(
T element, RetraceClassResult classResult) {
return retraceFieldOrReturnType(element)
.flatMap(
@@ -166,7 +169,7 @@
return Stream.of(proxy -> proxy.setRetracedFieldOrReturnType(RetracedTypeImpl.createVoid()));
} else {
TypeReference typeReference = Reference.typeFromTypeName(elementOrReturnType);
- RetraceTypeResultImpl retraceTypeResult = retracer.retraceType(typeReference);
+ RetraceTypeResult retraceTypeResult = retracer.retraceType(typeReference);
return retraceTypeResult.stream()
.map(
type ->
@@ -184,7 +187,7 @@
if (!element.hasMethodArguments()) {
return Stream.of(noEffect -> {});
}
- List<RetraceTypeResultImpl> retracedResults =
+ List<RetraceTypeResult> retracedResults =
Arrays.stream(element.getMethodArguments().split(","))
.map(typeName -> retracer.retraceType(Reference.typeFromTypeName(typeName)))
.collect(Collectors.toList());
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
index f649598..5a76e44 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementStringProxy.java
@@ -10,11 +10,11 @@
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.RetraceStackTraceProxy;
import com.android.tools.r8.retrace.RetracedClass;
import com.android.tools.r8.retrace.RetracedField;
import com.android.tools.r8.retrace.RetracedType;
import com.android.tools.r8.retrace.StackTraceElementProxy;
-import com.android.tools.r8.retrace.internal.StackTraceElementProxyRetracerImpl.RetraceStackTraceProxyImpl;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.StringUtils.BraceType;
import com.android.tools.r8.utils.TriFunction;
@@ -136,7 +136,7 @@
}
public String toRetracedItem(
- RetraceStackTraceProxyImpl<StackTraceElementStringProxy> retracedProxy,
+ RetraceStackTraceProxy<StackTraceElementStringProxy> retracedProxy,
boolean printAmbiguous,
boolean verbose) {
StringBuilder sb = new StringBuilder();
@@ -334,7 +334,7 @@
protected final int startIndex;
protected final int endIndex;
private final TriFunction<
- RetraceStackTraceProxyImpl<StackTraceElementStringProxy>,
+ RetraceStackTraceProxy<StackTraceElementStringProxy>,
StackTraceElementStringProxy,
Boolean,
String>
@@ -344,7 +344,7 @@
int startIndex,
int endIndex,
TriFunction<
- RetraceStackTraceProxyImpl<StackTraceElementStringProxy>,
+ RetraceStackTraceProxy<StackTraceElementStringProxy>,
StackTraceElementStringProxy,
Boolean,
String>
@@ -367,7 +367,7 @@
int startIndex,
int endIndex,
TriFunction<
- RetraceStackTraceProxyImpl<StackTraceElementStringProxy>,
+ RetraceStackTraceProxy<StackTraceElementStringProxy>,
StackTraceElementStringProxy,
Boolean,
String>
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationFixer.java b/src/main/java/com/android/tools/r8/shaking/AnnotationFixer.java
index a67af38..4fdad6a 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationFixer.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationFixer.java
@@ -4,15 +4,21 @@
package com.android.tools.r8.shaking;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationElement;
import com.android.tools.r8.graph.DexEncodedAnnotation;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
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.DexItemBasedValueString;
+import com.android.tools.r8.graph.DexValue.DexValueAnnotation;
import com.android.tools.r8.graph.DexValue.DexValueArray;
+import com.android.tools.r8.graph.DexValue.DexValueEnum;
import com.android.tools.r8.graph.DexValue.DexValueType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.utils.ArrayUtils;
@@ -56,27 +62,65 @@
}
private DexAnnotationElement rewriteAnnotationElement(DexAnnotationElement original) {
- DexValue rewrittenValue = rewriteValue(original.value);
+ DexValue rewrittenValue = rewriteComplexValue(original.value);
if (rewrittenValue != original.value) {
return new DexAnnotationElement(original.name, rewrittenValue);
}
return original;
}
- private DexValue rewriteValue(DexValue value) {
- if (value.isDexValueType()) {
+ private DexValue rewriteComplexValue(DexValue value) {
+ if (value.isDexValueArray()) {
+ DexValue[] originalValues = value.asDexValueArray().getValues();
+ DexValue[] rewrittenValues =
+ ArrayUtils.map(DexValue[].class, originalValues, this::rewriteComplexValue);
+ if (rewrittenValues != originalValues) {
+ return new DexValueArray(rewrittenValues);
+ }
+ } else if (value.isDexValueAnnotation()) {
+ DexValueAnnotation original = value.asDexValueAnnotation();
+ DexEncodedAnnotation rewritten = rewriteEncodedAnnotation(original.getValue());
+ if (original.value == rewritten) {
+ return value;
+ }
+ return new DexValueAnnotation(rewritten);
+ }
+ return rewriteNestedValue(value);
+ }
+
+ private DexValue rewriteNestedValue(DexValue value) {
+ if (value.isDexItemBasedValueString()) {
+ DexItemBasedValueString valueString = value.asDexItemBasedValueString();
+ DexReference original = valueString.value;
+ DexReference rewritten = lens.lookupReference(original);
+ if (original != rewritten) {
+ return new DexItemBasedValueString(rewritten, valueString.getNameComputationInfo());
+ }
+ } else if (value.isDexValueEnum()) {
+ DexField original = value.asDexValueEnum().value;
+ DexField rewritten = lens.lookupField(original);
+ if (original != rewritten) {
+ return new DexValueEnum(rewritten);
+ }
+ } else if (value.isDexValueField()) {
+ throw new Unreachable("Unexpected field in annotation");
+ } else if (value.isDexValueMethod()) {
+ throw new Unreachable("Unexpected method in annotation");
+ } else if (value.isDexValueMethodHandle()) {
+ throw new Unreachable("Unexpected method handle in annotation");
+ } else if (value.isDexValueMethodType()) {
+ throw new Unreachable("Unexpected method type in annotation");
+ } else if (value.isDexValueString()) {
+ // If we identified references in the string it would be a DexItemBasedValueString.
+ } else if (value.isDexValueType()) {
DexType originalType = value.asDexValueType().value;
DexType rewrittenType = lens.lookupType(originalType);
if (rewrittenType != originalType) {
return new DexValueType(rewrittenType);
}
- } else if (value.isDexValueArray()) {
- DexValue[] originalValues = value.asDexValueArray().getValues();
- DexValue[] rewrittenValues =
- ArrayUtils.map(DexValue[].class, originalValues, this::rewriteValue);
- if (rewrittenValues != originalValues) {
- return new DexValueArray(rewrittenValues);
- }
+ } else {
+ // Assert that we have not forgotten a value.
+ assert !value.isNestedDexValue();
}
return value;
}
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 7f93d17..9061db8 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -267,16 +267,14 @@
verify();
}
- private AppInfoWithLiveness(
- AppInfoWithLiveness previous, CommittedItems committedItems, Set<DexType> removedTypes) {
+ private AppInfoWithLiveness(AppInfoWithLiveness previous, CommittedItems committedItems) {
this(
committedItems,
previous.getClassToFeatureSplitMap(),
previous.getMainDexClasses(),
previous.deadProtoTypes,
previous.missingTypes,
- CollectionUtils.mergeSets(
- Sets.difference(previous.liveTypes, removedTypes), committedItems.getCommittedTypes()),
+ CollectionUtils.mergeSets(previous.liveTypes, committedItems.getCommittedTypes()),
previous.targetedMethods,
previous.failedResolutionTargets,
previous.bootstrapMethods,
@@ -925,8 +923,14 @@
* Returns a copy of this AppInfoWithLiveness where the set of classes is pruned using the given
* DexApplication object.
*/
+ @Override
public AppInfoWithLiveness prunedCopyFrom(PrunedItems prunedItems) {
+ assert getClass() == AppInfoWithLiveness.class;
assert checkIfObsolete();
+ if (prunedItems.isEmpty()) {
+ assert app() == prunedItems.getPrunedApp();
+ return this;
+ }
if (prunedItems.hasRemovedClasses()) {
// Rebuild the hierarchy.
objectAllocationInfoCollection.mutate(
@@ -937,9 +941,8 @@
return new AppInfoWithLiveness(this, prunedItems);
}
- public AppInfoWithLiveness rebuildWithLiveness(
- CommittedItems committedItems, Set<DexType> removedTypes) {
- return new AppInfoWithLiveness(this, committedItems, removedTypes);
+ public AppInfoWithLiveness rebuildWithLiveness(CommittedItems committedItems) {
+ return new AppInfoWithLiveness(this, committedItems);
}
public AppInfoWithLiveness rewrittenWithLens(
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexClasses.java b/src/main/java/com/android/tools/r8/shaking/MainDexClasses.java
index 4e6b632..9c1c529 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexClasses.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexClasses.java
@@ -89,6 +89,9 @@
}
public MainDexClasses withoutPrunedItems(PrunedItems prunedItems) {
+ if (prunedItems.isEmpty()) {
+ return this;
+ }
MainDexClasses mainDexClassesAfterPruning = createEmptyMainDexClasses();
for (DexType mainDexClass : mainDexClasses) {
if (!prunedItems.getRemovedClasses().contains(mainDexClass)) {
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 6f7d998..e049766 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -15,12 +15,10 @@
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexClass.FieldSetter;
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.DexItemFactory;
import com.android.tools.r8.graph.DexMember;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
@@ -42,6 +40,7 @@
import com.android.tools.r8.graph.RewrittenPrototypeDescription;
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
+import com.android.tools.r8.graph.TreeFixerBase;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.ir.code.Invoke.Type;
@@ -654,7 +653,8 @@
timing.begin("fixup");
VerticalClassMergerGraphLens lens =
- new TreeFixer(appView, lensBuilder, verticallyMergedClasses, synthesizedBridges)
+ new VerticalClassMergerTreeFixer(
+ appView, lensBuilder, verticallyMergedClasses, synthesizedBridges)
.fixupTypeReferences();
KeepInfoCollection keepInfo = appView.appInfo().getKeepInfo();
keepInfo.mutate(mutator -> mutator.removeKeepInfoForPrunedItems(mergedClasses.keySet()));
@@ -1465,23 +1465,20 @@
method.accessFlags.setPrivate();
}
- private static class TreeFixer {
+ private static class VerticalClassMergerTreeFixer extends TreeFixerBase {
private final AppView<AppInfoWithLiveness> appView;
- private final DexItemFactory dexItemFactory;
private final VerticalClassMergerGraphLens.Builder lensBuilder;
private final VerticallyMergedClasses mergedClasses;
private final List<SynthesizedBridgeCode> synthesizedBridges;
- private final Map<DexProto, DexProto> protoFixupCache = new IdentityHashMap<>();
-
- TreeFixer(
+ VerticalClassMergerTreeFixer(
AppView<AppInfoWithLiveness> appView,
VerticalClassMergerGraphLens.Builder lensBuilder,
VerticallyMergedClasses mergedClasses,
List<SynthesizedBridgeCode> synthesizedBridges) {
+ super(appView);
this.appView = appView;
- this.dexItemFactory = appView.dexItemFactory();
this.lensBuilder =
VerticalClassMergerGraphLens.Builder.createBuilderForFixup(lensBuilder, mergedClasses);
this.mergedClasses = mergedClasses;
@@ -1492,11 +1489,11 @@
// Globally substitute merged class types in protos and holders.
for (DexProgramClass clazz : appView.appInfo().classes()) {
clazz.getMethodCollection().replaceMethods(this::fixupMethod);
- fixupFields(clazz.staticFields(), clazz::setStaticField);
- fixupFields(clazz.instanceFields(), clazz::setInstanceField);
+ clazz.setStaticFields(fixupFields(clazz.staticFields()));
+ clazz.setInstanceFields(fixupFields(clazz.instanceFields()));
}
for (SynthesizedBridgeCode synthesizedBridge : synthesizedBridges) {
- synthesizedBridge.updateMethodSignatures(this::fixupMethod);
+ synthesizedBridge.updateMethodSignatures(this::fixupMethodReference);
}
VerticalClassMergerGraphLens lens = lensBuilder.build(appView, mergedClasses);
if (lens != null) {
@@ -1505,85 +1502,45 @@
return lens;
}
- private DexEncodedMethod fixupMethod(DexEncodedMethod method) {
- DexMethod methodReference = method.method;
- DexMethod newMethodReference = fixupMethod(methodReference);
- if (newMethodReference != methodReference) {
- if (!lensBuilder.hasOriginalSignatureMappingFor(newMethodReference)) {
- lensBuilder
- .map(methodReference, newMethodReference)
- .recordMove(methodReference, newMethodReference);
- }
- DexEncodedMethod newMethod = method.toTypeSubstitutedMethod(newMethodReference);
- if (newMethod.isNonPrivateVirtualMethod()) {
- // Since we changed the return type or one of the parameters, this method cannot be a
- // classpath or library method override, since we only class merge program classes.
- assert !method.isLibraryMethodOverride().isTrue();
- newMethod.setLibraryMethodOverride(OptionalBool.FALSE);
- }
- return newMethod;
- }
- return method;
- }
-
- private void fixupFields(List<DexEncodedField> fields, FieldSetter setter) {
- if (fields == null) {
- return;
- }
- for (int i = 0; i < fields.size(); i++) {
- DexEncodedField encodedField = fields.get(i);
- DexField field = encodedField.field;
- DexType newType = fixupType(field.type);
- DexType newHolder = fixupType(field.holder);
- DexField newField = dexItemFactory.createField(newHolder, newType, field.name);
- if (newField != encodedField.field) {
- if (!lensBuilder.hasOriginalSignatureMappingFor(newField)) {
- lensBuilder.map(field, newField);
- }
- setter.setField(i, encodedField.toTypeSubstitutedField(newField));
- }
- }
- }
-
- private DexMethod fixupMethod(DexMethod method) {
- return dexItemFactory.createMethod(
- fixupType(method.holder), fixupProto(method.proto), method.name);
- }
-
- private DexProto fixupProto(DexProto proto) {
- DexProto result = protoFixupCache.get(proto);
- if (result == null) {
- DexType returnType = fixupType(proto.returnType);
- DexType[] arguments = fixupTypes(proto.parameters.values);
- result = dexItemFactory.createProto(returnType, arguments);
- protoFixupCache.put(proto, result);
- }
- return result;
- }
-
- private DexType fixupType(DexType type) {
- if (type.isArrayType()) {
- DexType base = type.toBaseType(dexItemFactory);
- DexType fixed = fixupType(base);
- if (base == fixed) {
- return type;
- }
- return type.replaceBaseType(fixed, dexItemFactory);
- }
- if (type.isClassType()) {
- while (mergedClasses.hasBeenMergedIntoSubtype(type)) {
- type = mergedClasses.getTargetFor(type);
- }
+ @Override
+ public DexType mapClassType(DexType type) {
+ while (mergedClasses.hasBeenMergedIntoSubtype(type)) {
+ type = mergedClasses.getTargetFor(type);
}
return type;
}
- private DexType[] fixupTypes(DexType[] types) {
- DexType[] result = new DexType[types.length];
- for (int i = 0; i < result.length; i++) {
- result[i] = fixupType(types[i]);
+ @Override
+ public void recordClassChange(DexType from, DexType to) {
+ // Fixup of classes is not used so no class type should change.
+ throw new Unreachable();
+ }
+
+ @Override
+ public void recordFieldChange(DexField from, DexField to) {
+ if (!lensBuilder.hasOriginalSignatureMappingFor(to)) {
+ lensBuilder.map(from, to);
}
- return result;
+ }
+
+ @Override
+ public void recordMethodChange(DexMethod from, DexMethod to) {
+ if (!lensBuilder.hasOriginalSignatureMappingFor(to)) {
+ lensBuilder.map(from, to).recordMove(from, to);
+ }
+ }
+
+ @Override
+ public DexEncodedMethod recordMethodChange(
+ DexEncodedMethod method, DexEncodedMethod newMethod) {
+ recordMethodChange(method.method, newMethod.method);
+ if (newMethod.isNonPrivateVirtualMethod()) {
+ // Since we changed the return type or one of the parameters, this method cannot be a
+ // classpath or library method override, since we only class merge program classes.
+ assert !method.isLibraryMethodOverride().isTrue();
+ newMethod.setLibraryMethodOverride(OptionalBool.FALSE);
+ }
+ return newMethod;
}
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 6fa737b..c18b29d 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.GraphLens.Builder;
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
+import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.shaking.MainDexClasses;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -39,11 +40,11 @@
public static class Result {
public final CommittedItems commit;
- public final ImmutableSet<DexType> removedSyntheticClasses;
+ public final PrunedItems prunedItems;
- public Result(CommittedItems commit, ImmutableSet<DexType> removedSyntheticClasses) {
+ public Result(CommittedItems commit, PrunedItems prunedItems) {
this.commit = commit;
- this.removedSyntheticClasses = removedSyntheticClasses;
+ this.prunedItems = prunedItems;
}
}
@@ -126,6 +127,17 @@
appView.setGraphLens(lensBuilder.build(options.itemFactory, graphLens));
assert appView.appInfo().getMainDexClasses() == mainDexClasses;
+
+ Set<DexType> finalSyntheticTypes = Sets.newIdentityHashSet();
+ finalSyntheticClasses.forEach(clazz -> finalSyntheticTypes.add(clazz.getType()));
+
+ Set<DexType> prunedSynthetics = Sets.newIdentityHashSet();
+ for (DexType type : syntheticItems.keySet()) {
+ if (!finalSyntheticTypes.contains(type)) {
+ prunedSynthetics.add(type);
+ }
+ }
+
return new Result(
new CommittedItems(
SyntheticItems.INVALID_ID_AFTER_SYNTHETIC_FINALIZATION,
@@ -133,7 +145,7 @@
legacySyntheticTypes,
ImmutableMap.of(),
ImmutableList.of()),
- syntheticItems.keySet());
+ PrunedItems.builder().setPrunedApp(app).addRemovedClasses(prunedSynthetics).build());
}
private boolean verifyNoNestedSynthetics() {
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 175a39e..dfbeff6 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -331,6 +331,9 @@
public boolean quiet = false;
// Throw exception if there is a warning about invalid debug info.
public boolean invalidDebugInfoFatal = false;
+ // Don't gracefully recover from invalid debug info.
+ public boolean invalidDebugInfoStrict =
+ System.getProperty("com.android.tools.r8.strictdebuginfo") != null;
// When dexsplitting we ignore main dex classes missing in the application. These will be
// fused together by play store when shipped for pre-L devices.
@@ -1335,6 +1338,13 @@
public boolean enableGeneratedMessageLiteBuilderShrinking = false;
public boolean traverseOneOfAndRepeatedProtoFields = false;
public boolean enableEnumLiteProtoShrinking = false;
+ // Breaks the Chrome build if this is not enabled because of MethodToInvoke switchMaps.
+ // See b/174530756 for more details.
+ public boolean enableProtoEnumSwitchMapShrinking = true;
+
+ public boolean enableRemoveProtoEnumSwitchMap() {
+ return isProtoShrinkingEnabled() && enableProtoEnumSwitchMapShrinking;
+ }
public boolean isProtoShrinkingEnabled() {
return enableGeneratedExtensionRegistryShrinking
@@ -1343,7 +1353,7 @@
|| enableEnumLiteProtoShrinking;
}
- public boolean isProtoEnumShrinkingEnabled() {
+ public boolean isEnumLiteProtoShrinkingEnabled() {
return enableEnumLiteProtoShrinking;
}
}
diff --git a/src/main/keep.txt b/src/main/keep.txt
index f8bbc12..82f6965 100644
--- a/src/main/keep.txt
+++ b/src/main/keep.txt
@@ -20,6 +20,7 @@
-keepattributes SourceFile, LineNumberTable, InnerClasses, EnclosingMethod, Exceptions, Signature
+-keepparameternames
-keeppackagenames com.android.tools.r8
-repackageclasses com.android.tools.r8.internal
diff --git a/src/test/java/com/android/tools/r8/SingleTestRunResult.java b/src/test/java/com/android/tools/r8/SingleTestRunResult.java
index c03c8e05..7f04357 100644
--- a/src/test/java/com/android/tools/r8/SingleTestRunResult.java
+++ b/src/test/java/com/android/tools/r8/SingleTestRunResult.java
@@ -8,6 +8,7 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
+import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.naming.retrace.StackTrace;
import com.android.tools.r8.utils.AndroidApp;
@@ -16,6 +17,8 @@
import java.io.IOException;
import java.io.PrintStream;
import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
import org.hamcrest.Matcher;
public abstract class SingleTestRunResult<RR extends SingleTestRunResult<RR>>
@@ -23,6 +26,7 @@
protected final AndroidApp app;
private final TestRuntime runtime;
private final ProcessResult result;
+ private boolean executedSatisfyingRuntime = false;
public SingleTestRunResult(AndroidApp app, TestRuntime runtime, ProcessResult result) {
this.app = app;
@@ -169,4 +173,19 @@
ps.println(sb.toString());
return self();
}
+
+ public RR forDexRuntimeSatisfying(Predicate<DexVm.Version> predicate, Consumer<RR> action) {
+ if (runtime.isDex() && predicate.test(runtime.asDex().getVm().getVersion())) {
+ action.accept(self());
+ executedSatisfyingRuntime = true;
+ }
+ return self();
+ }
+
+ public RR otherwise(Consumer<RR> action) {
+ if (!executedSatisfyingRuntime) {
+ action.accept(self());
+ }
+ return self();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/TestRunResult.java b/src/test/java/com/android/tools/r8/TestRunResult.java
index 849e7d7..c775215 100644
--- a/src/test/java/com/android/tools/r8/TestRunResult.java
+++ b/src/test/java/com/android/tools/r8/TestRunResult.java
@@ -81,13 +81,6 @@
return assertFailure();
}
- public RR assertFailureWithErrorThatMatchesIf(boolean condition, Matcher<String> matcher) {
- if (condition) {
- return assertFailureWithErrorThatMatches(matcher);
- }
- return self();
- }
-
public RR assertFailureWithOutput(String expected) {
assertStdoutMatches(is(expected));
return assertFailure();
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileDeprecatedAttribute.java b/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileDeprecatedAttribute.java
index 0eac295..563ba1c 100644
--- a/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileDeprecatedAttribute.java
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileDeprecatedAttribute.java
@@ -8,7 +8,9 @@
import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.ClassFileConsumer.ForwardingConsumer;
import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -116,6 +118,28 @@
}
}
+ @Test
+ public void testR8() throws Exception {
+ R8FullTestBuilder builder =
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class)
+ .addKeepClassAndMembersRules(TestClass.class)
+ .addKeepAllAttributes()
+ .setMinApi(parameters.getApiLevel());
+ if (parameters.isCfRuntime()) {
+ builder.setProgramConsumer(
+ new ForwardingConsumer(null) {
+ @Override
+ public void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) {
+ checkDeprecatedAttributes(data.getBuffer());
+ }
+ });
+ }
+ builder
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
@Deprecated
public static class TestClass {
@Deprecated public Object object = new Object();
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/LargeEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/LargeEnumUnboxingTest.java
new file mode 100644
index 0000000..66c861a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/LargeEnumUnboxingTest.java
@@ -0,0 +1,246 @@
+// 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.enumunboxing;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.StringUtils;
+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 LargeEnumUnboxingTest extends EnumUnboxingTestBase {
+
+ private static final String EXPECTED_RESULT =
+ StringUtils.lines(
+ "B1falsetruefalse1",
+ "B2falsetruefalse2",
+ "B3falsetruefalse3",
+ "B4falsetruefalse4",
+ "B5falsetruefalse5",
+ "B6falsetruefalse6",
+ "B7falsetruefalse7",
+ "B8falsetruefalse8",
+ "B9falsetruefalse9",
+ "B10falsetruefalse10",
+ "B11falsetruefalse11",
+ "B12falsetruefalse12",
+ "B13falsetruefalse13",
+ "E1falsefalsetrue14",
+ "E2falsefalsetrue15",
+ "E3falsefalsetrue16",
+ "E4falsefalsetrue17",
+ "E5falsefalsetrue18",
+ "E6falsefalsetrue19",
+ "E7falsefalsetrue20",
+ "E8falsefalsetrue21",
+ "E9falsefalsetrue22",
+ "E10falsefalsetrue23",
+ "E11falsefalsetrue24",
+ "E12falsefalsetrue25",
+ "E13falsefalsetrue26",
+ "E14falsefalsetrue27",
+ "E15falsefalsetrue28",
+ "G1falsefalsefalse29",
+ "G2falsefalsefalse30",
+ "G3falsefalsefalse31",
+ "G4falsefalsefalse32",
+ "G5falsefalsefalse33",
+ "G6falsefalsefalse34",
+ "G7falsefalsefalse35",
+ "G8falsefalsefalse36",
+ "G9falsefalsefalse37",
+ "I1truefalsefalse38",
+ "I2truefalsefalse39",
+ "I3truefalsefalse40",
+ "I4truefalsefalse41",
+ "I5truefalsefalse42",
+ "I6truefalsefalse43",
+ "I7truefalsefalse44",
+ "I8truefalsefalse45",
+ "I9truefalsefalse46",
+ "I10truefalsefalse47",
+ "I11truefalsefalse48",
+ "I12truefalsefalse49",
+ "I13truefalsefalse50",
+ "I14truefalsefalse51",
+ "I15truefalsefalse52",
+ "I16truefalsefalse53",
+ "J1falsefalsefalse54",
+ "J2falsefalsefalse55");
+
+ private final TestParameters parameters;
+ private final boolean enumValueOptimization;
+ private final EnumKeepRules enumKeepRules;
+
+ @Parameters(name = "{0} valueOpt: {1} keep: {2}")
+ public static List<Object[]> data() {
+ return enumUnboxingTestParameters();
+ }
+
+ public LargeEnumUnboxingTest(
+ TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
+ this.parameters = parameters;
+ this.enumValueOptimization = enumValueOptimization;
+ this.enumKeepRules = enumKeepRules;
+ }
+
+ @Test
+ public void testEnumUnboxing() throws Exception {
+ Class<?> mainClass = Main.class;
+ testForR8(parameters.getBackend())
+ .addProgramClasses(mainClass, LargeEnum.class)
+ .addKeepMainRule(mainClass)
+ .addKeepRules(enumKeepRules.getKeepRules())
+ .enableNeverClassInliningAnnotations()
+ .addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
+ .allowDiagnosticInfoMessages()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectDiagnosticMessages(
+ m -> assertEnumIsUnboxed(LargeEnum.class, mainClass.getSimpleName(), m))
+ .run(parameters.getRuntime(), mainClass)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @NeverClassInline
+ enum LargeEnum {
+ B1("1"),
+ B2("2"),
+ B3("3"),
+ B4("4"),
+ B5("5"),
+ B6("6"),
+ B7("7"),
+ B8("8"),
+ B9("9"),
+ B10("10"),
+ B11("11"),
+ B12("12"),
+ B13("13"),
+
+ E1("14"),
+ E2("15"),
+ E3("16"),
+ E4("17"),
+ E5("18"),
+ E6("19"),
+ E7("20"),
+ E8("21"),
+ E9("22"),
+ E10("23"),
+ E11("24"),
+ E12("25"),
+ E13("26"),
+ E14("27"),
+ E15("28"),
+
+ G1("29"),
+ G2("30"),
+ G3("31"),
+ G4("32"),
+ G5("33"),
+ G6("34"),
+ G7("35"),
+ G8("36"),
+ G9("37"),
+
+ I1("38"),
+ I2("39"),
+ I3("40"),
+ I4("41"),
+ I5("42"),
+ I6("43"),
+ I7("44"),
+ I8("45"),
+ I9("46"),
+ I10("47"),
+ I11("48"),
+ I12("49"),
+ I13("50"),
+ I14("51"),
+ I15("52"),
+ I16("53"),
+
+ J1("54"),
+ J2("55");
+
+ private final String num;
+
+ LargeEnum(String num) {
+ this.num = num;
+ }
+
+ public String getNum() {
+ return num;
+ }
+
+ public boolean isI() {
+ return this == I1
+ || this == I2
+ || this == I3
+ || this == I4
+ || this == I5
+ || this == I6
+ || this == I7
+ || this == I8
+ || this == I9
+ || this == I10
+ || this == I11
+ || this == I12
+ || this == I13
+ || this == I14
+ || this == I15
+ || this == I16;
+ }
+
+ public boolean isB() {
+ return this == B1
+ || this == B2
+ || this == B3
+ || this == B4
+ || this == B5
+ || this == B6
+ || this == B7
+ || this == B8
+ || this == B9
+ || this == B10
+ || this == B11
+ || this == B12
+ || this == B13;
+ }
+
+ public boolean isE() {
+ return this == E1
+ || this == E2
+ || this == E3
+ || this == E4
+ || this == E5
+ || this == E6
+ || this == E7
+ || this == E8
+ || this == E9
+ || this == E10
+ || this == E11
+ || this == E12
+ || this == E13
+ || this == E14
+ || this == E15;
+ }
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ for (LargeEnum value : LargeEnum.values()) {
+ System.out.println(
+ value.toString() + value.isI() + value.isB() + value.isE() + value.getNum());
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/jasmin/InvalidDebugInfoTests.java b/src/test/java/com/android/tools/r8/jasmin/InvalidDebugInfoTests.java
index 9394995..edef7bf 100644
--- a/src/test/java/com/android/tools/r8/jasmin/InvalidDebugInfoTests.java
+++ b/src/test/java/com/android/tools/r8/jasmin/InvalidDebugInfoTests.java
@@ -3,20 +3,87 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.jasmin;
-import static org.junit.Assert.assertEquals;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.debuginfo.DebugInfoInspector;
import com.android.tools.r8.jasmin.JasminBuilder.ClassFileVersion;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.hamcrest.Matcher;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
-public class InvalidDebugInfoTests extends JasminTestBase {
+@RunWith(Parameterized.class)
+public class InvalidDebugInfoTests extends TestBase {
+
+ @Parameters(name = "{0}, strict:{1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build(),
+ BooleanUtils.values());
+ }
+
+ private final TestParameters parameters;
+ private final boolean strict;
+
+ public InvalidDebugInfoTests(TestParameters parameters, boolean strict) {
+ this.parameters = parameters;
+ this.strict = strict;
+ }
+
+ private void optionsModification(InternalOptions options) {
+ options.invalidDebugInfoStrict = strict;
+ options.testing.forceIRForCfToCfDesugar = true;
+ }
+
+ private final Matcher<Diagnostic> cfNotSupportedDiagnostic =
+ diagnosticMessage(
+ containsString("Compiling to Java class files with D8 is not officially supported"));
+
+ private void assertInvalidTypeMessage(TestDiagnosticMessages diagnostics) {
+ assertInvalidInfoMessages(diagnostics, "Attempt to define local of type");
+ }
+
+ private void assertUninitializedLocalMessage(TestDiagnosticMessages diagnostics) {
+ assertInvalidInfoMessages(diagnostics, "Local refers to uninitialized register");
+ }
+
+ private void assertInvalidInfoMessages(TestDiagnosticMessages diagnostics, String message) {
+ if (parameters.isCfRuntime()) {
+ diagnostics.assertNoErrors();
+ diagnostics.assertWarningsMatch(cfNotSupportedDiagnostic);
+ } else {
+ diagnostics.assertOnlyInfos();
+ }
+ // The reporting of invalid debug info issues three info items:
+ diagnostics.assertInfosMatch(
+ ImmutableList.of(
+ diagnosticMessage(containsString("Stripped invalid locals information")),
+ diagnosticMessage(containsString(message)),
+ diagnosticMessage(containsString("sign of using an outdated Java toolchain"))));
+ }
+
+ private void assertNoMessages(TestDiagnosticMessages diagnostics) {
+ if (parameters.isCfRuntime()) {
+ diagnostics.assertOnlyWarnings();
+ diagnostics.assertWarningsMatch(cfNotSupportedDiagnostic);
+ } else {
+ diagnostics.assertNoMessages();
+ }
+ }
// This is a regression test for invalid live-ranges of locals generated by some old Java
// compilers. The issue is that a local slot may have been initialized outside the live-scope of
@@ -75,10 +142,26 @@
" return");
String expected = "42" + ToolHelper.LINE_SEPARATOR + "0" + ToolHelper.LINE_SEPARATOR;
- String javaResult = runOnJava(builder, clazz.name);
- assertEquals(expected, javaResult);
- String artResult = runOnArtD8(builder, clazz.name);
- assertEquals(expected, artResult);
+ if (parameters.isCfRuntime()) {
+ testForJvm()
+ .addProgramClassFileData(builder.buildClasses())
+ .run(parameters.getRuntime(), clazz.name)
+ .assertSuccessWithOutput(expected);
+ }
+ testForD8(parameters.getBackend())
+ .addProgramClassFileData(builder.buildClasses())
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(this::optionsModification)
+ .compileWithExpectedDiagnostics(
+ diagnostics -> {
+ if (strict) {
+ assertUninitializedLocalMessage(diagnostics);
+ } else {
+ assertNoMessages(diagnostics);
+ }
+ })
+ .run(parameters.getRuntime(), clazz.name)
+ .assertSuccessWithOutput(expected);
}
// Regression test to check that we properly add UninitializedLocal SSA values for methods that
@@ -124,10 +207,26 @@
" return");
String expected = "42" + ToolHelper.LINE_SEPARATOR;
- String javaResult = runOnJava(builder, clazz.name);
- assertEquals(expected, javaResult);
- String artResult = runOnArtD8(builder, clazz.name);
- assertEquals(expected, artResult);
+ if (parameters.isCfRuntime()) {
+ testForJvm()
+ .addProgramClassFileData(builder.buildClasses())
+ .run(parameters.getRuntime(), clazz.name)
+ .assertSuccessWithOutput(expected);
+ }
+ testForD8(parameters.getBackend())
+ .addProgramClassFileData(builder.buildClasses())
+ .addOptionsModification(this::optionsModification)
+ .setMinApi(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(
+ diagnostics -> {
+ if (strict) {
+ assertUninitializedLocalMessage(diagnostics);
+ } else {
+ assertNoMessages(diagnostics);
+ }
+ })
+ .run(parameters.getRuntime(), clazz.name)
+ .assertSuccessWithOutput(expected);
}
@Test
@@ -165,13 +264,29 @@
" return");
String expected = StringUtils.lines("42", "7.5");
- String javaResult = runOnJava(builder, clazz.name);
- assertEquals(expected, javaResult);
- AndroidApp app = compileWithD8(builder);
- String artResult = runOnArt(app, clazz.name);
- assertEquals(expected, artResult);
- DebugInfoInspector info = new DebugInfoInspector(app, clazz.name, method);
- assertFalse(info.hasLocalsInfo());
+ if (parameters.isCfRuntime()) {
+ testForJvm()
+ .addProgramClassFileData(builder.buildClasses())
+ .run(parameters.getRuntime(), clazz.name)
+ .assertSuccessWithOutput(expected);
+ }
+ testForD8(parameters.getBackend())
+ .addProgramClassFileData(builder.buildClasses())
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(this::optionsModification)
+ .compileWithExpectedDiagnostics(
+ diagnostics -> {
+ if (strict) {
+ assertUninitializedLocalMessage(diagnostics);
+ } else {
+ assertInvalidTypeMessage(diagnostics);
+ }
+ })
+ .run(parameters.getRuntime(), clazz.name)
+ .assertSuccessWithOutput(expected)
+ .inspect(
+ inspector ->
+ assertFalse(inspector.clazz(clazz.name).method(method).hasLocalVariableTable()));
}
@Test
@@ -208,13 +323,22 @@
" return");
String expected = StringUtils.lines("42", "7.5");
- String javaResult = runOnJava(builder, clazz.name);
- assertEquals(expected, javaResult);
- AndroidApp app = compileWithD8(builder);
- String artResult = runOnArt(app, clazz.name);
- assertEquals(expected, artResult);
- DebugInfoInspector info = new DebugInfoInspector(app, clazz.name, method);
- assertFalse(info.hasLocalsInfo());
+ if (parameters.isCfRuntime()) {
+ testForJvm()
+ .addProgramClassFileData(builder.buildClasses())
+ .run(parameters.getRuntime(), clazz.name)
+ .assertSuccessWithOutput(expected);
+ }
+ testForD8(parameters.getBackend())
+ .addProgramClassFileData(builder.buildClasses())
+ .addOptionsModification(this::optionsModification)
+ .setMinApi(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(this::assertInvalidTypeMessage)
+ .run(parameters.getRuntime(), clazz.name)
+ .assertSuccessWithOutput(expected)
+ .inspect(
+ inspector ->
+ assertFalse(inspector.clazz(clazz.name).method(method).hasLocalVariableTable()));
}
@Test
@@ -258,13 +382,29 @@
" return");
String expected = StringUtils.lines("42");
- String javaResult = runOnJava(builder, clazz.name);
- assertEquals(expected, javaResult);
- AndroidApp app = compileWithD8(builder);
- String artResult = runOnArt(app, clazz.name);
- assertEquals(expected, artResult);
- DebugInfoInspector info = new DebugInfoInspector(app, clazz.name, method);
- assertFalse(info.hasLocalsInfo());
+ if (parameters.isCfRuntime()) {
+ testForJvm()
+ .addProgramClassFileData(builder.buildClasses())
+ .run(parameters.getRuntime(), clazz.name)
+ .assertSuccessWithOutput(expected);
+ }
+ testForD8(parameters.getBackend())
+ .addProgramClassFileData(builder.buildClasses())
+ .addOptionsModification(this::optionsModification)
+ .setMinApi(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(
+ diagnostics -> {
+ if (strict) {
+ assertUninitializedLocalMessage(diagnostics);
+ } else {
+ assertInvalidTypeMessage(diagnostics);
+ }
+ })
+ .run(parameters.getRuntime(), clazz.name)
+ .assertSuccessWithOutput(expected)
+ .inspect(
+ inspector ->
+ assertFalse(inspector.clazz(clazz.name).method(method).hasLocalVariableTable()));
}
@Test
@@ -309,14 +449,24 @@
" return");
String expected = StringUtils.lines("42");
- String javaResult = runOnJava(builder, clazz.name);
- assertEquals(expected, javaResult);
- AndroidApp app = compileWithD8(builder);
- String artResult = runOnArt(app, clazz.name);
- assertEquals(expected, artResult);
- DebugInfoInspector info = new DebugInfoInspector(app, clazz.name, method);
- // Note: This code is actually invalid debug info, but we do not reject it because both types
- // are reference types. If we ever change that we should update this test.
- assertTrue(info.hasLocalsInfo());
+ if (parameters.isCfRuntime()) {
+ testForJvm()
+ .addProgramClassFileData(builder.buildClasses())
+ .run(parameters.getRuntime(), clazz.name)
+ .assertSuccessWithOutput(expected);
+ }
+ testForD8(parameters.getBackend())
+ .addProgramClassFileData(builder.buildClasses())
+ .addOptionsModification(this::optionsModification)
+ .setMinApi(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(this::assertNoMessages)
+ .run(parameters.getRuntime(), clazz.name)
+ .assertSuccessWithOutput(expected)
+ .inspect(
+ inspector -> {
+ // Note: This code is actually invalid debug info, but we do not reject it because
+ // both types are reference types. If we change that we should update this test.
+ assertTrue(inspector.clazz(clazz.name).method(method).hasLocalVariableTable());
+ });
}
}
diff --git a/src/test/java/com/android/tools/r8/repackage/EnumAndIdentifierBasedStringInAnnotationTest.java b/src/test/java/com/android/tools/r8/repackage/EnumAndIdentifierBasedStringInAnnotationTest.java
new file mode 100644
index 0000000..f542e6e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/EnumAndIdentifierBasedStringInAnnotationTest.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.repackage;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestParameters;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class EnumAndIdentifierBasedStringInAnnotationTest extends RepackageTestBase {
+
+ public EnumAndIdentifierBasedStringInAnnotationTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addInnerClasses(EnumAndIdentifierBasedStringInAnnotationTest.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("TEST_ONE");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(EnumAndIdentifierBasedStringInAnnotationTest.class)
+ .addKeepMainRule(Main.class)
+ .addKeepClassAndMembersRules(MyAnnotation.class)
+ .addKeepClassAndMembersRulesWithAllowObfuscation(Enum.class)
+ .addKeepRules("-keepclassmembers,allowshrinking class ** { *; }")
+ .setMinApi(parameters.getApiLevel())
+ .addKeepRuntimeVisibleAnnotations()
+ .apply(this::configureRepackaging)
+ .compile()
+ .inspect(inspector -> assertThat(inspector.clazz(Enum.class), isPresentAndRenamed()))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("TEST_ONE");
+ }
+
+ public enum Enum {
+ TEST_ONE,
+ TEST_TWO
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.TYPE})
+ public @interface MyAnnotation {
+
+ Enum value() default Enum.TEST_TWO;
+ }
+
+ @MyAnnotation(value = Enum.TEST_ONE)
+ public static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(Main.class.getAnnotation(MyAnnotation.class).value());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceFieldTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceFieldTests.java
index 2b90ffc..3ff24fa 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceFieldTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceFieldTests.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.retrace;
import com.android.tools.r8.TestDiagnosticMessagesImpl;
-import com.android.tools.r8.retrace.internal.RetracerImpl;
import com.android.tools.r8.retrace.mappings.FieldsWithSameMinifiedNameMapping;
import com.android.tools.r8.retrace.mappings.MappingForTest;
import java.util.function.Consumer;
@@ -21,6 +20,6 @@
private void runRetraceTest(MappingForTest mappingForTest, Consumer<Retracer> inspection) {
inspection.accept(
- RetracerImpl.create(mappingForTest::mapping, new TestDiagnosticMessagesImpl()));
+ Retracer.createDefault(mappingForTest::mapping, new TestDiagnosticMessagesImpl()));
}
}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index 82d3915..0c7dd7a 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -15,7 +15,6 @@
import com.android.tools.r8.TestDiagnosticMessagesImpl;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.retrace.internal.RetraceAbortException;
-import com.android.tools.r8.retrace.internal.RetracerImpl;
import com.android.tools.r8.retrace.stacktraces.ActualBotStackTraceBase;
import com.android.tools.r8.retrace.stacktraces.ActualIdentityStackTrace;
import com.android.tools.r8.retrace.stacktraces.ActualRetraceBotStackTrace;
@@ -24,6 +23,7 @@
import com.android.tools.r8.retrace.stacktraces.AmbiguousWithMultipleLineMappingsStackTrace;
import com.android.tools.r8.retrace.stacktraces.AmbiguousWithSignatureNonVerboseStackTrace;
import com.android.tools.r8.retrace.stacktraces.CircularReferenceStackTrace;
+import com.android.tools.r8.retrace.stacktraces.ColonInFileNameStackTrace;
import com.android.tools.r8.retrace.stacktraces.FileNameExtensionStackTrace;
import com.android.tools.r8.retrace.stacktraces.InlineFileNameStackTrace;
import com.android.tools.r8.retrace.stacktraces.InlineFileNameWithInnerClassesStackTrace;
@@ -32,6 +32,7 @@
import com.android.tools.r8.retrace.stacktraces.InlineWithLineNumbersStackTrace;
import com.android.tools.r8.retrace.stacktraces.InvalidStackTrace;
import com.android.tools.r8.retrace.stacktraces.MemberFieldOverlapStackTrace;
+import com.android.tools.r8.retrace.stacktraces.MultipleDotsInFileNameStackTrace;
import com.android.tools.r8.retrace.stacktraces.NamedModuleStackTrace;
import com.android.tools.r8.retrace.stacktraces.NullStackTrace;
import com.android.tools.r8.retrace.stacktraces.ObfucatedExceptionClassStackTrace;
@@ -39,6 +40,7 @@
import com.android.tools.r8.retrace.stacktraces.RetraceAssertionErrorStackTrace;
import com.android.tools.r8.retrace.stacktraces.StackTraceForTest;
import com.android.tools.r8.retrace.stacktraces.SuppressedStackTrace;
+import com.android.tools.r8.retrace.stacktraces.UnicodeInFileNameStackTrace;
import com.android.tools.r8.retrace.stacktraces.UnknownSourceStackTrace;
import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableList;
@@ -194,16 +196,31 @@
}
@Test
- public void testMemberFieldOverlapStackTrace() throws Exception {
+ public void testColonInSourceFileNameStackTrace() {
+ runRetraceTest(new ColonInFileNameStackTrace());
+ }
+
+ @Test
+ public void testMultipleDotsInFileNameStackTrace() {
+ runRetraceTest(new MultipleDotsInFileNameStackTrace());
+ }
+
+ @Test
+ public void testUnicodeInFileNameStackTrace() {
+ runRetraceTest(new UnicodeInFileNameStackTrace());
+ }
+
+ @Test
+ public void testMemberFieldOverlapStackTrace() {
MemberFieldOverlapStackTrace stackTraceForTest = new MemberFieldOverlapStackTrace();
runRetraceTest(stackTraceForTest);
inspectRetraceTest(stackTraceForTest, stackTraceForTest::inspectField);
}
private void inspectRetraceTest(
- StackTraceForTest stackTraceForTest, Consumer<Retracer> inspection) throws Exception {
+ StackTraceForTest stackTraceForTest, Consumer<Retracer> inspection) {
inspection.accept(
- RetracerImpl.create(stackTraceForTest::mapping, new TestDiagnosticMessagesImpl()));
+ Retracer.createDefault(stackTraceForTest::mapping, new TestDiagnosticMessagesImpl()));
}
private TestDiagnosticMessagesImpl runRetraceTest(StackTraceForTest stackTraceForTest) {
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/ColonInFileNameStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/ColonInFileNameStackTrace.java
new file mode 100644
index 0000000..0bdb870
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/ColonInFileNameStackTrace.java
@@ -0,0 +1,35 @@
+// 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.retrace.stacktraces;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+
+public class ColonInFileNameStackTrace implements StackTraceForTest {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return ImmutableList.of(" at a.s(:foo::bar:1)");
+ }
+
+ @Override
+ public String mapping() {
+ return "some.Class -> a:\n"
+ // Sourcefile metadata.
+ + "# {\"id\":\"sourceFile\",\"fileName\":\"Class.kt\"}\n"
+ + " 1:3:int strawberry(int):99:101 -> s\n"
+ + " 4:5:int mango(float):121:122 -> s\n";
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return ImmutableList.of(" at some.Class.strawberry(Class.kt:99)");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/MultipleDotsInFileNameStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/MultipleDotsInFileNameStackTrace.java
new file mode 100644
index 0000000..a67335f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/MultipleDotsInFileNameStackTrace.java
@@ -0,0 +1,35 @@
+// 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.retrace.stacktraces;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+
+public class MultipleDotsInFileNameStackTrace implements StackTraceForTest {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return ImmutableList.of(" at a.s(foo.bar.baz:1)");
+ }
+
+ @Override
+ public String mapping() {
+ return "some.Class -> a:\n"
+ // Sourcefile metadata.
+ + "# {\"id\":\"sourceFile\",\"fileName\":\"Class.kt\"}\n"
+ + " 1:3:int strawberry(int):99:101 -> s\n"
+ + " 4:5:int mango(float):121:122 -> s\n";
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return ImmutableList.of(" at some.Class.strawberry(Class.kt:99)");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/UnicodeInFileNameStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/UnicodeInFileNameStackTrace.java
new file mode 100644
index 0000000..5090c39
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/UnicodeInFileNameStackTrace.java
@@ -0,0 +1,35 @@
+// 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.retrace.stacktraces;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+
+public class UnicodeInFileNameStackTrace implements StackTraceForTest {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return ImmutableList.of(" at a.s(Blåbærgrød.jàvà:1)");
+ }
+
+ @Override
+ public String mapping() {
+ return "some.Class -> a:\n"
+ // Sourcefile metadata.
+ + "# {\"id\":\"sourceFile\",\"fileName\":\"Class.kt\"}\n"
+ + " 1:3:int strawberry(int):99:101 -> s\n"
+ + " 4:5:int mango(float):121:122 -> s\n";
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return ImmutableList.of(" at some.Class.strawberry(Class.kt:99)");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+}
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 d2518b6..d70775f 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
@@ -36,7 +36,6 @@
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.retrace.Retracer;
import com.android.tools.r8.retrace.internal.DirectClassNameMapperProguardMapProducer;
-import com.android.tools.r8.retrace.internal.RetracerImpl;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.BiMapContainer;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -464,7 +463,7 @@
}
public Retracer retrace() {
- return RetracerImpl.create(
+ return Retracer.createDefault(
new InternalProguardMapProducer(
mapping == null ? ClassNameMapper.builder().build() : mapping),
new TestDiagnosticMessagesImpl());