Fix clobbering in mapping when recording sourcefile for pruned classes
Bug: b/279702361
Change-Id: Ia2895fa2597ba5264ee420c2a09a5eacc37f7509
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
index b9bc6bc..c568a31 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -76,6 +76,10 @@
}
}
+ public boolean hasMapping(String obfuscatedName) {
+ return mapping.containsKey(obfuscatedName);
+ }
+
@Override
public ClassNameMapper build() {
return new ClassNameMapper(
diff --git a/src/main/java/com/android/tools/r8/naming/SeedMapper.java b/src/main/java/com/android/tools/r8/naming/SeedMapper.java
index 45f7a5b..c94b593 100644
--- a/src/main/java/com/android/tools/r8/naming/SeedMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/SeedMapper.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.utils.DescriptorUtils.descriptorToInternalName;
import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
import static com.android.tools.r8.utils.DescriptorUtils.javaTypeToDescriptor;
+import static com.android.tools.r8.utils.positions.MappedPositionToClassNameMapperBuilder.getPrunedInlinedClassObfuscatedPrefix;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.naming.MemberNaming.Signature;
@@ -56,8 +57,11 @@
ClassNamingForMapApplier.Builder classNamingBuilder =
ClassNamingForMapApplier.builder(
renamedDescriptorName, originalDescriptor, position, reporter);
- if (map.put(originalDescriptor, classNamingBuilder) != null) {
- reporter.error(ProguardMapError.duplicateSourceClass(originalName, position));
+ // Disallow renaming to a synthetic chosen name for pruned classes.
+ if (!renamedName.startsWith(getPrunedInlinedClassObfuscatedPrefix())) {
+ if (map.put(originalDescriptor, classNamingBuilder) != null) {
+ reporter.error(ProguardMapError.duplicateSourceClass(originalDescriptor, position));
+ }
}
return classNamingBuilder;
}
diff --git a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
index ae0eb0e..d2d30bd 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
@@ -43,6 +43,7 @@
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.IntBox;
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.OneShotCollectionConsumer;
@@ -63,6 +64,7 @@
public class MappedPositionToClassNameMapperBuilder {
private static final int MAX_LINE_NUMBER = 65535;
+ private static final String PRUNED_INLINED_CLASS_OBFUSCATED_PREFIX = "R8$$REMOVED$$CLASS$$";
private final OriginalSourceFiles originalSourceFiles;
private final AppView<?> appView;
@@ -85,6 +87,10 @@
appView.options().getMapFileVersion().toMapVersionMappingInformation());
}
+ public static String getPrunedInlinedClassObfuscatedPrefix() {
+ return PRUNED_INLINED_CLASS_OBFUSCATED_PREFIX;
+ }
+
public static int getMaxLineNumber() {
return MAX_LINE_NUMBER;
}
@@ -104,16 +110,24 @@
private void addSourceFileLinesForPrunedClasses() {
// Add all pruned inline classes to the mapping to recover source files.
List<Entry<DexType, String>> prunedEntries = new ArrayList<>(prunedInlinedClasses.entrySet());
+ IntBox counter = new IntBox();
prunedEntries.sort(Entry.comparingByKey());
prunedEntries.forEach(
entry -> {
DexType holder = entry.getKey();
- assert appView.appInfo().definitionForWithoutExistenceAssert(holder) == null;
+ assert appView.appInfo().definitionForWithoutExistenceAssert(holder) == null
+ || !appView.appInfo().definitionForWithoutExistenceAssert(holder).isProgramClass();
String typeName = holder.toSourceString();
String sourceFile = entry.getValue();
+ // We have to pick a right-hand side destination that does not overlap with an existing
+ // mapping.
+ String renamedName;
+ do {
+ renamedName = PRUNED_INLINED_CLASS_OBFUSCATED_PREFIX + counter.getAndIncrement();
+ } while (classNameMapperBuilder.hasMapping(renamedName));
classNameMapperBuilder
.classNamingBuilder(
- typeName, typeName, com.android.tools.r8.position.Position.UNKNOWN)
+ renamedName, typeName, com.android.tools.r8.position.Position.UNKNOWN)
.addMappingInformation(FileNameInformation.build(sourceFile), Unreachable::raise);
});
}
diff --git a/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java b/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
index fa9de18..5b85343 100644
--- a/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
@@ -139,7 +139,7 @@
private static void verifyUniqueNaming(CodeInspector inspector, List<String> klasses) {
Set<String> renamedNames = Sets.newHashSet();
for (String klass : klasses) {
- String finalName = inspector.clazz(klass).getFinalName();
+ String finalName = inspector.getObfuscatedTypeName(klass);
assertFalse(renamedNames.contains(finalName));
renamedNames.add(finalName);
}
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 b569a57..814a684 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
@@ -336,15 +336,17 @@
String name = DescriptorUtils.descriptorToJavaType(descriptor);
ClassNamingForNameMapper naming = null;
if (mapping != null) {
- String obfuscated = originalToObfuscatedMapping.get(name);
- if (obfuscated != null) {
- naming = mapping.getClassNaming(obfuscated);
- name = obfuscated;
+ // Figure out if the name is an already obfuscated name. It is important to look up by
+ // residual name first to ensure that we do not accidentally find a class that has been
+ // pruned.
+ String original = obfuscatedToOriginalMapping.get(name);
+ if (original != null) {
+ naming = mapping.getClassNaming(name);
} else {
- // Figure out if the name is an already obfuscated name.
- String original = obfuscatedToOriginalMapping.get(name);
- if (original != null) {
- naming = mapping.getClassNaming(name);
+ String obfuscated = originalToObfuscatedMapping.get(name);
+ if (obfuscated != null) {
+ naming = mapping.getClassNaming(obfuscated);
+ name = obfuscated;
}
}
}
@@ -400,7 +402,7 @@
return clazz.method(method);
}
- String getObfuscatedTypeName(String originalTypeName) {
+ public String getObfuscatedTypeName(String originalTypeName) {
String obfuscatedTypeName = null;
if (mapping != null) {
obfuscatedTypeName = mapType(originalToObfuscatedMapping, originalTypeName);
@@ -408,7 +410,7 @@
return obfuscatedTypeName != null ? obfuscatedTypeName : originalTypeName;
}
- String getOriginalTypeName(String minifiedTypeName) {
+ public String getOriginalTypeName(String minifiedTypeName) {
String originalTypeName = null;
if (mapping != null) {
originalTypeName = mapType(obfuscatedToOriginalMapping, minifiedTypeName);