Stream the Proguard map to the consumer
Bug: 152939233
Change-Id: Ibba93a8b69294b2b0ffffd115209ac5935c9a286
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 4472714..e98d9b9 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.naming.MemberNaming.Signature;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.BiMapContainer;
+import com.android.tools.r8.utils.ChainableStringConsumer;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableMap;
@@ -23,16 +24,14 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-import java.io.StringWriter;
-import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
-import java.util.List;
+import java.util.Iterator;
import java.util.Map;
+import java.util.Map.Entry;
public class ClassNameMapper implements ProguardMap {
@@ -113,6 +112,10 @@
this.classNameMappings = builder.build();
}
+ private ClassNameMapper(ImmutableMap<String, ClassNamingForNameMapper> classNameMappings) {
+ this.classNameMappings = classNameMappings;
+ }
+
public Map<String, ClassNamingForNameMapper> getClassNameMappings() {
return classNameMappings;
}
@@ -182,26 +185,41 @@
return classNameMappings.isEmpty();
}
- public void write(Writer writer) throws IOException {
- // Sort classes by their original name such that the generated Proguard map is deterministic
- // (and easy to navigate manually).
- List<ClassNamingForNameMapper> classNamingForNameMappers =
- new ArrayList<>(classNameMappings.values());
- classNamingForNameMappers.sort(Comparator.comparing(x -> x.originalName));
- for (ClassNamingForNameMapper naming : classNamingForNameMappers) {
- naming.write(writer);
+ public ClassNameMapper sorted() {
+ ImmutableMap.Builder<String, ClassNamingForNameMapper> builder = ImmutableMap.builder();
+ builder.orderEntriesByValue(Comparator.comparing(x -> x.originalName));
+ classNameMappings.forEach(builder::put);
+ return new ClassNameMapper(builder.build());
+ }
+
+ public boolean verifyIsSorted() {
+ Iterator<Entry<String, ClassNamingForNameMapper>> iterator =
+ getClassNameMappings().entrySet().iterator();
+ Iterator<Entry<String, ClassNamingForNameMapper>> sortedIterator =
+ sorted().getClassNameMappings().entrySet().iterator();
+ while (iterator.hasNext()) {
+ Entry<String, ClassNamingForNameMapper> entry = iterator.next();
+ Entry<String, ClassNamingForNameMapper> otherEntry = sortedIterator.next();
+ assert entry.getKey().equals(otherEntry.getKey());
+ assert entry.getValue() == otherEntry.getValue();
+ }
+ return true;
+ }
+
+ public void write(ChainableStringConsumer consumer) {
+ // Classes should be sorted by their original name such that the generated Proguard map is
+ // deterministic (and easy to navigate manually).
+ assert verifyIsSorted();
+ for (ClassNamingForNameMapper naming : getClassNameMappings().values()) {
+ naming.write(consumer);
}
}
@Override
public String toString() {
- try {
- StringWriter writer = new StringWriter();
- write(writer);
- return writer.toString();
- } catch (IOException e) {
- return e.toString();
- }
+ StringBuilder builder = new StringBuilder();
+ write(ChainableStringConsumer.wrap(builder::append));
+ return builder.toString();
}
public BiMapContainer<String, String> getObfuscatedToOriginalMapping() {
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
index ee02d05..5a00fcb 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
@@ -7,12 +7,10 @@
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.MemberNaming.Signature;
import com.android.tools.r8.naming.MemberNaming.Signature.SignatureKind;
+import com.android.tools.r8.utils.ChainableStringConsumer;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -297,19 +295,11 @@
return methodMembers.values();
}
- void write(Writer writer) throws IOException {
- writer.append(originalName);
- writer.append(" -> ");
- writer.append(renamedName);
- writer.append(":\n");
+ void write(ChainableStringConsumer consumer) {
+ consumer.accept(originalName).accept(" -> ").accept(renamedName).accept(":\n");
- // First print non-method MemberNamings.
- forAllMemberNaming(
- m -> {
- if (!m.isMethodNaming()) {
- writer.append(" ").append(m.toString()).append('\n');
- }
- });
+ // First print field member namings.
+ forAllFieldNaming(m -> consumer.accept(" ").accept(m.toString()).accept("\n"));
// Sort MappedRanges by sequence number to restore construction order (original Proguard-map
// input).
@@ -319,19 +309,15 @@
}
mappedRangesSorted.sort(Comparator.comparingInt(range -> range.sequenceNumber));
for (MappedRange range : mappedRangesSorted) {
- writer.append(" ").append(range.toString()).append('\n');
+ consumer.accept(" ").accept(range.toString()).accept("\n");
}
}
@Override
public String toString() {
- try {
- StringWriter writer = new StringWriter();
- write(writer);
- return writer.toString();
- } catch (IOException e) {
- return e.toString();
- }
+ StringBuilder builder = new StringBuilder();
+ write(ChainableStringConsumer.wrap(builder::append));
+ return builder.toString();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
index d5b915b..ec947e8 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
@@ -3,10 +3,15 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.Version;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.ChainableStringConsumer;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.VersionProperties;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
@@ -34,11 +39,20 @@
}
private final ClassNameMapper classNameMapper;
+ private final StringConsumer consumer;
private final InternalOptions options;
+ private final Reporter reporter;
- public ProguardMapSupplier(ClassNameMapper classNameMapper, InternalOptions options) {
- this.classNameMapper = classNameMapper;
+ private ProguardMapSupplier(ClassNameMapper classNameMapper, InternalOptions options) {
+ assert classNameMapper != null;
+ assert !classNameMapper.isEmpty();
+ this.classNameMapper = classNameMapper.sorted();
+ this.consumer =
+ InternalOptions.assertionsEnabled()
+ ? new ProguardMapChecker(options.proguardMapConsumer)
+ : options.proguardMapConsumer;
this.options = options;
+ this.reporter = options.reporter;
}
public static ProguardMapSupplier create(
@@ -47,32 +61,25 @@
}
public ProguardMapId writeProguardMap() {
- String body = classNameMapper.toString();
- assert body != null;
- assert !body.trim().isEmpty();
- ProguardMapId id = computeProguardMapId(body);
- StringBuilder builder = new StringBuilder();
- writeMarker(builder, id);
- writeBody(builder, body);
- String proguardMapContent = builder.toString();
- assert validateProguardMapParses(proguardMapContent);
- ExceptionUtils.withConsumeResourceHandler(
- options.reporter, options.proguardMapConsumer, proguardMapContent);
- ExceptionUtils.withFinishedResourceHandler(options.reporter, options.proguardMapConsumer);
+ ProguardMapId id = computeProguardMapId();
+ writeMarker(id);
+ writeBody();
+ ExceptionUtils.withFinishedResourceHandler(reporter, consumer);
return id;
}
- private ProguardMapId computeProguardMapId(String body) {
- Hasher hasher = Hashing.murmur3_32().newHasher();
- body.codePoints().filter(c -> !Character.isWhitespace(c)).forEach(hasher::putInt);
- return new ProguardMapId(hasher.hash().toString().substring(0, PG_MAP_ID_LENGTH));
+ private ProguardMapId computeProguardMapId() {
+ ProguardMapIdBuilder builder = new ProguardMapIdBuilder();
+ classNameMapper.write(builder);
+ return builder.build();
}
- private void writeBody(StringBuilder builder, String body) {
- builder.append(body);
+ private void writeBody() {
+ classNameMapper.write(new ProguardMapWriter());
}
- private void writeMarker(StringBuilder builder, ProguardMapId id) {
+ private void writeMarker(ProguardMapId id) {
+ StringBuilder builder = new StringBuilder();
builder.append(
"# "
+ MARKER_KEY_COMPILER
@@ -94,15 +101,71 @@
builder.append("# " + MARKER_KEY_PG_MAP_ID + ": " + id.get() + "\n");
// Turn off linting of the mapping file in some build systems.
builder.append("# common_typos_disable" + "\n");
+ consumer.accept(builder.toString(), reporter);
}
- private static boolean validateProguardMapParses(String content) {
- try {
- ClassNameMapper.mapperFromString(content);
- } catch (IOException e) {
- e.printStackTrace();
- return false;
+ static class ProguardMapIdBuilder implements ChainableStringConsumer {
+
+ private final Hasher hasher = Hashing.murmur3_32().newHasher();
+
+ @Override
+ public ProguardMapIdBuilder accept(String string) {
+ for (int i = 0; i < string.length(); i++) {
+ char c = string.charAt(i);
+ if (!Character.isWhitespace(c)) {
+ hasher.putInt(c);
+ }
+ }
+ return this;
}
- return true;
+
+ public ProguardMapId build() {
+ return new ProguardMapId(hasher.hash().toString().substring(0, PG_MAP_ID_LENGTH));
+ }
+ }
+
+ class ProguardMapWriter implements ChainableStringConsumer {
+
+ @Override
+ public ProguardMapWriter accept(String string) {
+ consumer.accept(string, reporter);
+ return this;
+ }
+ }
+
+ static class ProguardMapChecker implements StringConsumer {
+
+ private final StringConsumer inner;
+ private final StringBuilder contents = new StringBuilder();
+
+ ProguardMapChecker(StringConsumer inner) {
+ if (!InternalOptions.assertionsEnabled()) {
+ // Make sure we never get here without assertions enabled.
+ throw new Unreachable();
+ }
+ this.inner = inner;
+ }
+
+ @Override
+ public void accept(String string, DiagnosticsHandler handler) {
+ inner.accept(string, handler);
+ contents.append(string);
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ inner.finished(handler);
+ assert validateProguardMapParses(contents.toString());
+ }
+
+ private static boolean validateProguardMapParses(String content) {
+ try {
+ ClassNameMapper.mapperFromString(content);
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+ return true;
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/ChainableConsumer.java b/src/main/java/com/android/tools/r8/utils/ChainableConsumer.java
new file mode 100644
index 0000000..7d69754
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ChainableConsumer.java
@@ -0,0 +1,10 @@
+// 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.utils;
+
+public interface ChainableConsumer<T> {
+
+ ChainableConsumer<T> accept(T value);
+}
diff --git a/src/main/java/com/android/tools/r8/utils/ChainableStringConsumer.java b/src/main/java/com/android/tools/r8/utils/ChainableStringConsumer.java
new file mode 100644
index 0000000..d1636c5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ChainableStringConsumer.java
@@ -0,0 +1,23 @@
+// 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.utils;
+
+import java.util.function.Consumer;
+
+public interface ChainableStringConsumer extends ChainableConsumer<String> {
+
+ @Override
+ ChainableStringConsumer accept(String string);
+
+ static ChainableStringConsumer wrap(Consumer<String> consumer) {
+ return new ChainableStringConsumer() {
+ @Override
+ public ChainableStringConsumer accept(String value) {
+ consumer.accept(value);
+ return this;
+ }
+ };
+ }
+}