Avoid repeatedly iterating all classes to generate prefix error message
Fixes: 245514955
Change-Id: I517a6002691f8bef2816018b7415a0613b611f0b
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 ebbecb0..7636cbd 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
@@ -155,6 +155,7 @@
// Use AtomicBoolean to satisfy TSAN checking (see b/153714743).
AtomicBoolean seenNotNeverMergePrefix = new AtomicBoolean();
AtomicBoolean seenNeverMergePrefix = new AtomicBoolean();
+ String conflictingPrefixesErrorMessage = null;
/**
* The argument `appView` is used to determine if whole program optimizations are allowed or not
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
index 532c129..172e90a 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
@@ -195,64 +195,68 @@
// TODO(b/168001352): Consider requiring that no 'never merge' prefix is ever seen as a
// passthrough object.
if (seenNeverMergePrefix.get() && seenNotNeverMergePrefix.get()) {
- StringBuilder message = new StringBuilder();
- message
- .append("Merging DEX file containing classes with prefix")
- .append(neverMerge.getPrefixes().size() > 1 ? "es " : " ");
- for (int i = 0; i < neverMerge.getPrefixes().size(); i++) {
- message
- .append("'")
- .append(neverMerge.getPrefixes().get(i).toString().substring(1).replace('/', '.'))
- .append("'")
- .append(i < neverMerge.getPrefixes().size() - 1 ? ", " : "");
- }
- if (!neverMerge.getExceptionPrefixes().isEmpty()) {
- message
- .append(" with other classes, except classes with prefix")
- .append(neverMerge.getExceptionPrefixes().size() > 1 ? "es " : " ");
- for (int i = 0; i < neverMerge.getExceptionPrefixes().size(); i++) {
- message
- .append("'")
- .append(
- neverMerge
- .getExceptionPrefixes()
- .get(i)
- .toString()
- .substring(1)
- .replace('/', '.'))
- .append("'")
- .append(i < neverMerge.getExceptionPrefixes().size() - 1 ? ", " : "");
- }
- message.append(",");
- } else {
- message.append(" with classes with any other prefixes");
- }
- message.append(" is not allowed: ");
- boolean first = true;
- int limit = 11;
- for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) {
- if (!clazz.type.descriptor.startsWith(neverMergePrefix)) {
- if (hasExceptionPrefix(clazz)) {
- continue;
- }
- if (limit-- < 0) {
- message.append("..");
- break;
- }
- if (first) {
- first = false;
- } else {
- message.append(", ");
- }
- message.append(clazz.type);
- }
- }
- message.append(".");
- throw new CompilationError(message.toString());
+ throw new CompilationError(getOrComputeConflictingPrefixesErrorMessage(neverMergePrefix));
}
}
}
+ private synchronized String getOrComputeConflictingPrefixesErrorMessage(
+ DexString neverMergePrefix) {
+ if (conflictingPrefixesErrorMessage != null) {
+ return conflictingPrefixesErrorMessage;
+ }
+ StringBuilder message = new StringBuilder();
+ message
+ .append("Merging DEX file containing classes with prefix")
+ .append(neverMerge.getPrefixes().size() > 1 ? "es " : " ");
+ for (int i = 0; i < neverMerge.getPrefixes().size(); i++) {
+ message
+ .append("'")
+ .append(neverMerge.getPrefixes().get(i).toString().substring(1).replace('/', '.'))
+ .append("'")
+ .append(i < neverMerge.getPrefixes().size() - 1 ? ", " : "");
+ }
+ if (!neverMerge.getExceptionPrefixes().isEmpty()) {
+ message
+ .append(" with other classes, except classes with prefix")
+ .append(neverMerge.getExceptionPrefixes().size() > 1 ? "es " : " ");
+ for (int i = 0; i < neverMerge.getExceptionPrefixes().size(); i++) {
+ message
+ .append("'")
+ .append(
+ neverMerge.getExceptionPrefixes().get(i).toString().substring(1).replace('/', '.'))
+ .append("'")
+ .append(i < neverMerge.getExceptionPrefixes().size() - 1 ? ", " : "");
+ }
+ message.append(",");
+ } else {
+ message.append(" with classes with any other prefixes");
+ }
+ message.append(" is not allowed: ");
+ boolean first = true;
+ int limit = 11;
+ for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) {
+ if (!clazz.type.descriptor.startsWith(neverMergePrefix)) {
+ if (hasExceptionPrefix(clazz)) {
+ continue;
+ }
+ if (limit-- < 0) {
+ message.append("..");
+ break;
+ }
+ if (first) {
+ first = false;
+ } else {
+ message.append(", ");
+ }
+ message.append(clazz.type);
+ }
+ }
+ message.append(".");
+ conflictingPrefixesErrorMessage = message.toString();
+ return conflictingPrefixesErrorMessage;
+ }
+
private boolean hasExceptionPrefix(DexProgramClass clazz) {
for (DexString exceptionPrefix : neverMerge.getExceptionPrefixes()) {
if (clazz.type.descriptor.startsWith(exceptionPrefix)) {