Reland "[Partition] Correctly capture partition keys and preamble in metadata"
This reverts commit 72e9e579ea3bac44b6f54769715430cb982b400d.
Bug: b/274735214
Change-Id: I0204cb4820784b83c57d87236f033aa4d7319802
diff --git a/src/main/java/com/android/tools/r8/dex/CompatByteBuffer.java b/src/main/java/com/android/tools/r8/dex/CompatByteBuffer.java
index 0d36470..533a5f5 100644
--- a/src/main/java/com/android/tools/r8/dex/CompatByteBuffer.java
+++ b/src/main/java/com/android/tools/r8/dex/CompatByteBuffer.java
@@ -7,6 +7,7 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
+import java.nio.charset.StandardCharsets;
/**
* In JDK 9 ByteBuffer ByteBuffer.position(int) started overriding Buffer Buffer.position(int) along
@@ -26,6 +27,10 @@
return new CompatByteBuffer(ByteBuffer.wrap(bytes));
}
+ public static CompatByteBuffer wrapOrNull(byte[] bytes) {
+ return bytes == null ? null : wrap(bytes);
+ }
+
private Buffer asBuffer() {
return buffer;
}
@@ -136,4 +141,34 @@
public void put(byte[] bytes) {
asByteBuffer().put(bytes);
}
+
+ // ----------------------------------------------------------------------------------------------
+ // Additional custom methods
+ // ----------------------------------------------------------------------------------------------
+
+ public int getUShort() {
+ return buffer.getShort() & 0xffff;
+ }
+
+ public byte[] getBytesOfUByteSize() {
+ int length = getUShort();
+ byte[] data = new byte[length];
+ get(data);
+ return data;
+ }
+
+ public String getUTFOfUByteSize() {
+ return new String(getBytesOfUByteSize(), StandardCharsets.UTF_8);
+ }
+
+ public byte[] getBytesOfIntSize() {
+ int length = buffer.getInt();
+ byte[] data = new byte[length];
+ get(data);
+ return data;
+ }
+
+ public String getUTFOfIntSize() {
+ return new String(getBytesOfIntSize(), StandardCharsets.UTF_8);
+ }
}
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 0ee8077..3d30621 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -29,7 +29,6 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@@ -40,6 +39,7 @@
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
public class ClassNameMapper implements ProguardMap {
@@ -51,6 +51,7 @@
public static class Builder extends ProguardMap.Builder {
private boolean buildPreamble = false;
+ private boolean addVersionAsPreamble = false;
private final List<String> preamble = new ArrayList<>();
private final Map<String, ClassNamingForNameMapper.Builder> mapping = new HashMap<>();
private final LinkedHashSet<MapVersionMappingInformation> mapVersions = new LinkedHashSet<>();
@@ -65,13 +66,18 @@
return classNamingBuilder;
}
- Builder setBuildPreamble(boolean buildPreamble) {
+ public Builder setBuildPreamble(boolean buildPreamble) {
this.buildPreamble = buildPreamble;
return this;
}
+ public Builder setAddVersionAsPreamble(boolean addVersionAsPreamble) {
+ this.addVersionAsPreamble = addVersionAsPreamble;
+ return this;
+ }
+
@Override
- void addPreambleLine(String line) {
+ public void addPreambleLine(String line) {
if (buildPreamble) {
preamble.add(line);
}
@@ -93,6 +99,9 @@
@Override
public ProguardMap.Builder setCurrentMapVersion(MapVersionMappingInformation mapVersion) {
mapVersions.add(mapVersion);
+ if (addVersionAsPreamble) {
+ addPreambleLine("# " + mapVersion.serialize());
+ }
return this;
}
}
@@ -148,14 +157,15 @@
DiagnosticsHandler diagnosticsHandler,
boolean allowEmptyMappedRanges,
boolean allowExperimentalMapping,
- boolean readPreamble)
+ boolean buildPreamble)
throws IOException {
- return mapperFromLineReader(
+ return mapperFromLineReaderWithFiltering(
LineReader.fromBufferedReader(CharSource.wrap(contents).openBufferedStream()),
+ MapVersion.MAP_VERSION_NONE,
diagnosticsHandler,
allowEmptyMappedRanges,
allowExperimentalMapping,
- readPreamble);
+ builder -> builder.setBuildPreamble(buildPreamble));
}
private static ClassNameMapper mapperFromBufferedReader(
@@ -170,31 +180,13 @@
boolean allowExperimentalMapping,
boolean buildPreamble)
throws IOException {
- return mapperFromLineReader(
+ return mapperFromLineReaderWithFiltering(
LineReader.fromBufferedReader(reader),
+ MapVersion.MAP_VERSION_NONE,
diagnosticsHandler,
allowEmptyMappedRanges,
allowExperimentalMapping,
- buildPreamble);
- }
-
- public static ClassNameMapper mapperFromLineReader(
- LineReader reader,
- DiagnosticsHandler diagnosticsHandler,
- boolean allowEmptyMappedRanges,
- boolean allowExperimentalMapping,
- boolean buildPreamble)
- throws IOException {
- try (ProguardMapReader proguardReader =
- new ProguardMapReader(
- reader,
- diagnosticsHandler != null ? diagnosticsHandler : new Reporter(),
- allowEmptyMappedRanges,
- allowExperimentalMapping)) {
- ClassNameMapper.Builder builder = ClassNameMapper.builder().setBuildPreamble(buildPreamble);
- proguardReader.parse(builder);
- return builder.build();
- }
+ builder -> builder.setBuildPreamble(buildPreamble));
}
public static ClassNameMapper mapperFromLineReaderWithFiltering(
@@ -202,7 +194,8 @@
MapVersion mapVersion,
DiagnosticsHandler diagnosticsHandler,
boolean allowEmptyMappedRanges,
- boolean allowExperimentalMapping)
+ boolean allowExperimentalMapping,
+ Consumer<ClassNameMapper.Builder> builderConsumer)
throws IOException {
try (ProguardMapReader proguardReader =
new ProguardMapReader(
@@ -212,6 +205,7 @@
allowExperimentalMapping,
mapVersion)) {
ClassNameMapper.Builder builder = ClassNameMapper.builder();
+ builderConsumer.accept(builder);
proguardReader.parse(builder);
return builder.build();
}
@@ -239,7 +233,7 @@
return classNameMappings;
}
- public Collection<String> getPreamble() {
+ public List<String> getPreamble() {
return preamble;
}
@@ -350,7 +344,7 @@
}
public boolean isEmpty() {
- return classNameMappings.isEmpty();
+ return classNameMappings.isEmpty() && preamble.isEmpty();
}
public ClassNameMapper sorted() {
diff --git a/src/main/java/com/android/tools/r8/retrace/PartitionedToProguardMappingConverter.java b/src/main/java/com/android/tools/r8/retrace/PartitionedToProguardMappingConverter.java
index b6181dc..0da8f47 100644
--- a/src/main/java/com/android/tools/r8/retrace/PartitionedToProguardMappingConverter.java
+++ b/src/main/java/com/android/tools/r8/retrace/PartitionedToProguardMappingConverter.java
@@ -8,12 +8,13 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.StringConsumer;
+import com.android.tools.r8.dex.CompatByteBuffer;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.LineReader;
import com.android.tools.r8.naming.MapVersion;
import com.android.tools.r8.retrace.internal.MappingPartitionMetadataInternal;
+import com.android.tools.r8.retrace.internal.MetadataAdditionalInfo;
import com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.ProguardMapReaderWithFilteringInputBuffer;
-import com.android.tools.r8.retrace.internal.RetracePartitionException;
import com.android.tools.r8.utils.ChainableStringConsumer;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -39,12 +40,21 @@
public void run() throws RetracePartitionException {
MappingPartitionMetadataInternal metadataInternal =
MappingPartitionMetadataInternal.deserialize(
- metadata, MapVersion.MAP_VERSION_UNKNOWN, diagnosticsHandler);
+ CompatByteBuffer.wrapOrNull(metadata),
+ MapVersion.MAP_VERSION_UNKNOWN,
+ diagnosticsHandler);
if (!metadataInternal.canGetPartitionKeys()) {
throw new RetracePartitionException("Cannot obtain all partition keys from metadata");
}
- // TODO(b/274893426): Account for preamble.
- ClassNameMapper classNameMapper = ClassNameMapper.builder().build();
+ ClassNameMapper.Builder builder = ClassNameMapper.builder();
+ if (metadataInternal.canGetAdditionalInfo()) {
+ MetadataAdditionalInfo additionalInfo = metadataInternal.getAdditionalInfo();
+ if (additionalInfo.hasPreamble()) {
+ builder.setBuildPreamble(true);
+ additionalInfo.getPreamble().forEach(builder::addPreambleLine);
+ }
+ }
+ ClassNameMapper classNameMapper = builder.build();
for (String partitionKey : metadataInternal.getPartitionKeys()) {
LineReader reader =
new ProguardMapReaderWithFilteringInputBuffer(
@@ -52,13 +62,22 @@
try {
classNameMapper =
ClassNameMapper.mapperFromLineReaderWithFiltering(
- reader, metadataInternal.getMapVersion(), diagnosticsHandler, true, true)
+ reader,
+ metadataInternal.getMapVersion(),
+ diagnosticsHandler,
+ true,
+ true,
+ partitionBuilder -> partitionBuilder.setBuildPreamble(true))
.combine(classNameMapper);
} catch (IOException e) {
throw new RetracePartitionException(e);
}
}
- classNameMapper.sorted().write(new ProguardMapWriter(consumer, diagnosticsHandler));
+ ProguardMapWriter consumer = new ProguardMapWriter(this.consumer, diagnosticsHandler);
+ classNameMapper
+ .getPreamble()
+ .forEach(preambleLine -> consumer.accept(preambleLine).accept("\n"));
+ classNameMapper.sorted().write(consumer);
}
private static class ProguardMapWriter implements ChainableStringConsumer {
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetracePartitionException.java b/src/main/java/com/android/tools/r8/retrace/RetracePartitionException.java
similarity index 90%
rename from src/main/java/com/android/tools/r8/retrace/internal/RetracePartitionException.java
rename to src/main/java/com/android/tools/r8/retrace/RetracePartitionException.java
index 7e6e924..5712f0d 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetracePartitionException.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetracePartitionException.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.Keep;
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionKeyStrategy.java b/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionKeyStrategy.java
index b31d984..6b050c6 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionKeyStrategy.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionKeyStrategy.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.retrace.internal;
public enum MappingPartitionKeyStrategy {
+ UNKNOWN(-1),
OBFUSCATED_TYPE_NAME_AS_KEY(0),
OBFUSCATED_TYPE_NAME_AS_KEY_WITH_PARTITIONS(1);
@@ -16,6 +17,17 @@
this.serializedKey = serializedKey;
}
+ public static MappingPartitionKeyStrategy getByKey(int serializedKey) {
+ switch (serializedKey) {
+ case 0:
+ return OBFUSCATED_TYPE_NAME_AS_KEY;
+ case 1:
+ return OBFUSCATED_TYPE_NAME_AS_KEY_WITH_PARTITIONS;
+ default:
+ return UNKNOWN;
+ }
+ }
+
public int getSerializedKey() {
return serializedKey;
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionMetadataInternal.java b/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionMetadataInternal.java
index 0cee013..9896856 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionMetadataInternal.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionMetadataInternal.java
@@ -6,11 +6,16 @@
import static com.android.tools.r8.retrace.internal.MappingPartitionKeyStrategy.OBFUSCATED_TYPE_NAME_AS_KEY;
import static com.android.tools.r8.retrace.internal.MappingPartitionKeyStrategy.OBFUSCATED_TYPE_NAME_AS_KEY_WITH_PARTITIONS;
+import static com.android.tools.r8.retrace.internal.MappingPartitionKeyStrategy.getByKey;
+import static com.android.tools.r8.utils.SerializationUtils.getZeroByte;
import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.dex.CompatByteBuffer;
import com.android.tools.r8.naming.MapVersion;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.retrace.MappingPartitionMetadata;
+import com.android.tools.r8.retrace.RetracePartitionException;
+import com.android.tools.r8.retrace.internal.MetadataAdditionalInfo.LazyMetadataAdditionalInfo;
import com.android.tools.r8.retrace.internal.MetadataPartitionCollection.LazyMetadataPartitionCollection;
import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.google.common.primitives.Ints;
@@ -33,7 +38,14 @@
return null;
}
- byte ZERO_BYTE = (byte) 0;
+ default boolean canGetAdditionalInfo() {
+ return false;
+ }
+
+ default MetadataAdditionalInfo getAdditionalInfo() {
+ return MetadataAdditionalInfo.create(null);
+ }
+
// Magic byte put into the metadata
byte[] MAGIC = new byte[] {(byte) 0xAA, (byte) 0xA8};
@@ -42,20 +54,25 @@
}
static MappingPartitionMetadataInternal deserialize(
- byte[] bytes, MapVersion fallBackMapVersion, DiagnosticsHandler diagnosticsHandler) {
- if (bytes == null) {
+ CompatByteBuffer buffer,
+ MapVersion fallBackMapVersion,
+ DiagnosticsHandler diagnosticsHandler) {
+ if (buffer == null) {
return ObfuscatedTypeNameAsKeyMetadata.create(fallBackMapVersion);
}
- if (bytes.length > 2) {
- if (startsWithMagic(bytes)) {
- int serializedKey =
- Ints.fromBytes(ZERO_BYTE, ZERO_BYTE, bytes[magicOffset()], bytes[magicOffset() + 1]);
- if (serializedKey == OBFUSCATED_TYPE_NAME_AS_KEY_WITH_PARTITIONS.getSerializedKey()) {
- return ObfuscatedTypeNameAsKeyMetadataWithPartitionNames.deserialize(bytes);
- }
- } else if (OBFUSCATED_TYPE_NAME_AS_KEY.getSerializedKey()
- == Ints.fromBytes(ZERO_BYTE, ZERO_BYTE, bytes[0], bytes[1])) {
- return ObfuscatedTypeNameAsKeyMetadata.deserialize(bytes);
+ if (buffer.remaining() > 2) {
+ int magicOrStrategyKey = buffer.getUShort();
+ if (magicOrStrategyKey == Ints.fromBytes(getZeroByte(), getZeroByte(), MAGIC[0], MAGIC[1])) {
+ magicOrStrategyKey = buffer.getShort();
+ }
+ switch (getByKey(magicOrStrategyKey)) {
+ case OBFUSCATED_TYPE_NAME_AS_KEY:
+ return ObfuscatedTypeNameAsKeyMetadata.deserialize(buffer);
+ case OBFUSCATED_TYPE_NAME_AS_KEY_WITH_PARTITIONS:
+ return ObfuscatedTypeNameAsKeyMetadataWithPartitionNames.deserialize(buffer);
+ default:
+ throw new RetracePartitionException(
+ "Could not find partition key strategy from serialized key: " + magicOrStrategyKey);
}
}
// If we arrived here then we could not deserialize the metadata.
@@ -65,18 +82,6 @@
throw exception;
}
- private static boolean startsWithMagic(byte[] bytes) {
- if (bytes.length < MAGIC.length) {
- return false;
- }
- for (int i = 0; i < MAGIC.length; i++) {
- if (bytes[i] != MAGIC[i]) {
- return false;
- }
- }
- return true;
- }
-
class ObfuscatedTypeNameAsKeyMetadata implements MappingPartitionMetadataInternal {
private final MapVersion mapVersion;
@@ -111,8 +116,9 @@
}
}
- public static ObfuscatedTypeNameAsKeyMetadata deserialize(byte[] bytes) {
- MapVersion mapVersion = MapVersion.fromName(new String(bytes, 2, bytes.length - 2));
+ public static ObfuscatedTypeNameAsKeyMetadata deserialize(CompatByteBuffer buffer) {
+ byte[] array = buffer.array();
+ MapVersion mapVersion = MapVersion.fromName(new String(array, 2, array.length - 2));
return create(mapVersion);
}
@@ -126,17 +132,23 @@
private final MapVersion mapVersion;
private final MetadataPartitionCollection metadataPartitionCollection;
+ private final MetadataAdditionalInfo metadataAdditionalInfo;
private ObfuscatedTypeNameAsKeyMetadataWithPartitionNames(
- MapVersion mapVersion, MetadataPartitionCollection metadataPartitionCollection) {
+ MapVersion mapVersion,
+ MetadataPartitionCollection metadataPartitionCollection,
+ MetadataAdditionalInfo metadataAdditionalInfo) {
this.mapVersion = mapVersion;
this.metadataPartitionCollection = metadataPartitionCollection;
+ this.metadataAdditionalInfo = metadataAdditionalInfo;
}
public static ObfuscatedTypeNameAsKeyMetadataWithPartitionNames create(
- MapVersion mapVersion, MetadataPartitionCollection metadataPartitionCollection) {
+ MapVersion mapVersion,
+ MetadataPartitionCollection metadataPartitionCollection,
+ MetadataAdditionalInfo metadataAdditionalInfo) {
return new ObfuscatedTypeNameAsKeyMetadataWithPartitionNames(
- mapVersion, metadataPartitionCollection);
+ mapVersion, metadataPartitionCollection, metadataAdditionalInfo);
}
@Override
@@ -159,8 +171,18 @@
return metadataPartitionCollection.getPartitionKeys();
}
+ @Override
+ public boolean canGetAdditionalInfo() {
+ return true;
+ }
+
+ @Override
+ public MetadataAdditionalInfo getAdditionalInfo() {
+ return metadataAdditionalInfo;
+ }
+
// The format is:
- // <type:short><map-version-length:short><map-version>[<partition_key>]
+ // <MAGIC><type:short><map-version-length:short><map-version>{partitions}{additionalinfo}
@Override
public byte[] getBytes() {
try {
@@ -168,10 +190,9 @@
DataOutputStream dataOutputStream = new DataOutputStream(temp);
dataOutputStream.write(MAGIC);
dataOutputStream.writeShort(OBFUSCATED_TYPE_NAME_AS_KEY_WITH_PARTITIONS.getSerializedKey());
- String name = mapVersion.getName();
- dataOutputStream.writeShort(name.length());
- dataOutputStream.writeBytes(name);
- dataOutputStream.write(metadataPartitionCollection.serialize());
+ dataOutputStream.writeUTF(mapVersion.getName());
+ metadataPartitionCollection.serialize(dataOutputStream);
+ metadataAdditionalInfo.serialize(dataOutputStream);
dataOutputStream.close();
return temp.toByteArray();
} catch (IOException e) {
@@ -179,14 +200,16 @@
}
}
- public static ObfuscatedTypeNameAsKeyMetadataWithPartitionNames deserialize(byte[] bytes) {
- int start = magicOffset();
- int length = Ints.fromBytes(ZERO_BYTE, ZERO_BYTE, bytes[start + 2], bytes[start + 3]);
- MapVersion mapVersion = MapVersion.fromName(new String(bytes, start + 4, length));
- int partitionCollectionStartIndex = start + 4 + length;
+ public static ObfuscatedTypeNameAsKeyMetadataWithPartitionNames deserialize(
+ CompatByteBuffer buffer) {
+ String utf = buffer.getUTFOfUByteSize();
+ MapVersion mapVersion = MapVersion.fromName(utf);
+ LazyMetadataPartitionCollection metadataPartitionCollection =
+ LazyMetadataPartitionCollection.create(buffer);
+ LazyMetadataAdditionalInfo lazyMetadataAdditionalInfo =
+ LazyMetadataAdditionalInfo.create(buffer);
return ObfuscatedTypeNameAsKeyMetadataWithPartitionNames.create(
- mapVersion,
- new LazyMetadataPartitionCollection(bytes, partitionCollectionStartIndex, bytes.length));
+ mapVersion, metadataPartitionCollection, lazyMetadataAdditionalInfo);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/MetadataAdditionalInfo.java b/src/main/java/com/android/tools/r8/retrace/internal/MetadataAdditionalInfo.java
new file mode 100644
index 0000000..706584b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/internal/MetadataAdditionalInfo.java
@@ -0,0 +1,121 @@
+// Copyright (c) 2023, 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.internal;
+
+import com.android.tools.r8.dex.CompatByteBuffer;
+import com.android.tools.r8.retrace.RetracePartitionException;
+import com.android.tools.r8.utils.SerializationUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+
+public class MetadataAdditionalInfo {
+
+ public enum AdditionalInfoTypes {
+ UNKNOWN(-1),
+ PREAMBLE(0);
+
+ private final int serializedKey;
+
+ AdditionalInfoTypes(int serializedKey) {
+ this.serializedKey = serializedKey;
+ }
+
+ static AdditionalInfoTypes getByKey(int serializedKey) {
+ if (serializedKey == 0) {
+ return PREAMBLE;
+ }
+ return UNKNOWN;
+ }
+ }
+
+ protected final List<String> preamble;
+
+ private MetadataAdditionalInfo(List<String> preamble) {
+ this.preamble = preamble;
+ }
+
+ public boolean hasPreamble() {
+ return preamble != null;
+ }
+
+ public Collection<String> getPreamble() {
+ return preamble;
+ }
+
+ // The serialized format is an extensible list where we first record the offsets for each data
+ // section and then emit the data.
+ // <total-size:int><number-of-elements:short>[<type-i:short><length-i:int><data-i>]
+ public void serialize(DataOutputStream dataOutputStream) throws IOException {
+ ByteArrayOutputStream temp = new ByteArrayOutputStream();
+ DataOutputStream additionalInfoStream = new DataOutputStream(temp);
+ additionalInfoStream.writeShort(1);
+ additionalInfoStream.writeShort(AdditionalInfoTypes.PREAMBLE.serializedKey);
+ SerializationUtils.writeUTFOfIntSize(additionalInfoStream, StringUtils.unixLines(preamble));
+ byte[] payload = temp.toByteArray();
+ dataOutputStream.writeInt(payload.length);
+ dataOutputStream.write(payload);
+ }
+
+ private static MetadataAdditionalInfo deserialize(byte[] bytes) {
+ CompatByteBuffer compatByteBuffer = CompatByteBuffer.wrap(bytes);
+ int numberOfElements = compatByteBuffer.getShort();
+ List<String> preamble = null;
+ for (int i = 0; i < numberOfElements; i++) {
+ // We are parsing <type:short><length:int><bytes>
+ int additionInfoTypeKey = compatByteBuffer.getShort();
+ AdditionalInfoTypes additionalInfoType = AdditionalInfoTypes.getByKey(additionInfoTypeKey);
+ if (additionalInfoType == AdditionalInfoTypes.PREAMBLE) {
+ preamble = StringUtils.splitLines(compatByteBuffer.getUTFOfIntSize());
+ } else {
+ throw new RetracePartitionException(
+ "Could not additional info from key: " + additionInfoTypeKey);
+ }
+ }
+ return new MetadataAdditionalInfo(preamble);
+ }
+
+ public static MetadataAdditionalInfo create(List<String> preamble) {
+ return new MetadataAdditionalInfo(preamble);
+ }
+
+ public static class LazyMetadataAdditionalInfo extends MetadataAdditionalInfo {
+
+ private byte[] bytes;
+ private MetadataAdditionalInfo metadataAdditionalInfo = null;
+
+ public LazyMetadataAdditionalInfo(byte[] bytes) {
+ super(null);
+ this.bytes = bytes;
+ }
+
+ @Override
+ public boolean hasPreamble() {
+ MetadataAdditionalInfo metadataAdditionalInfo = getMetadataAdditionalInfo();
+ return metadataAdditionalInfo != null && metadataAdditionalInfo.hasPreamble();
+ }
+
+ @Override
+ public Collection<String> getPreamble() {
+ MetadataAdditionalInfo metadataAdditionalInfo = getMetadataAdditionalInfo();
+ return metadataAdditionalInfo == null ? null : metadataAdditionalInfo.getPreamble();
+ }
+
+ private MetadataAdditionalInfo getMetadataAdditionalInfo() {
+ if (metadataAdditionalInfo == null) {
+ metadataAdditionalInfo = MetadataAdditionalInfo.deserialize(bytes);
+ bytes = null;
+ }
+ return metadataAdditionalInfo;
+ }
+
+ public static LazyMetadataAdditionalInfo create(CompatByteBuffer buffer) {
+ return new LazyMetadataAdditionalInfo(buffer.getBytesOfIntSize());
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/MetadataPartitionCollection.java b/src/main/java/com/android/tools/r8/retrace/internal/MetadataPartitionCollection.java
index aef50f0..3a3d271 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/MetadataPartitionCollection.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/MetadataPartitionCollection.java
@@ -4,7 +4,11 @@
package com.android.tools.r8.retrace.internal;
+import com.android.tools.r8.dex.CompatByteBuffer;
+import com.android.tools.r8.utils.SerializationUtils;
import com.android.tools.r8.utils.StringUtils;
+import java.io.DataOutputStream;
+import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Collections;
@@ -23,50 +27,43 @@
return partitionKeys;
}
- public byte[] serialize() {
- return StringUtils.join(SEPARATOR + "", partitionKeys).getBytes(StandardCharsets.UTF_8);
+ // The format is:
+ // <length-in-bytes:int><data>
+ public void serialize(DataOutputStream dataOutputStream) throws IOException {
+ SerializationUtils.writeUTFOfIntSize(
+ dataOutputStream, StringUtils.join(SEPARATOR + "", partitionKeys));
+ }
+
+ private static MetadataPartitionCollection deserialize(byte[] bytes) {
+ String allKeys = new String(bytes, StandardCharsets.UTF_8);
+ return create(StringUtils.split(allKeys, SEPARATOR));
}
public static MetadataPartitionCollection create(Collection<String> partitionKeys) {
return new MetadataPartitionCollection(partitionKeys);
}
- public static MetadataPartitionCollection createLazy(
- byte[] bytes, int partitionCollectionStartIndex, int partitionCollectionEndIndex) {
- return new LazyMetadataPartitionCollection(
- bytes, partitionCollectionStartIndex, partitionCollectionEndIndex);
- }
-
public static class LazyMetadataPartitionCollection extends MetadataPartitionCollection {
- private final byte[] bytes;
- private final int partitionCollectionStartIndex;
- private final int partitionCollectionEndIndex;
+ private byte[] bytes;
private MetadataPartitionCollection metadataPartitionCollection = null;
- public LazyMetadataPartitionCollection(
- byte[] bytes, int partitionCollectionStartIndex, int partitionCollectionEndIndex) {
+ private LazyMetadataPartitionCollection(byte[] bytes) {
super(Collections.emptyList());
this.bytes = bytes;
- this.partitionCollectionStartIndex = partitionCollectionStartIndex;
- this.partitionCollectionEndIndex = partitionCollectionEndIndex;
}
@Override
public Collection<String> getPartitionKeys() {
if (metadataPartitionCollection == null) {
- metadataPartitionCollection = deserialize();
+ metadataPartitionCollection = deserialize(bytes);
+ bytes = null;
}
return metadataPartitionCollection.getPartitionKeys();
}
- private MetadataPartitionCollection deserialize() {
- String allKeys =
- new String(
- bytes,
- partitionCollectionStartIndex,
- partitionCollectionEndIndex - partitionCollectionStartIndex);
- return create(StringUtils.split(allKeys, SEPARATOR));
+ public static LazyMetadataPartitionCollection create(CompatByteBuffer buffer) {
+ return new LazyMetadataPartitionCollection(buffer.getBytesOfIntSize());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierImpl.java
index 7d663fb..de94d1e 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierImpl.java
@@ -7,6 +7,7 @@
import static com.google.common.base.Predicates.alwaysTrue;
import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.dex.CompatByteBuffer;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.LineReader;
import com.android.tools.r8.naming.MapVersion;
@@ -66,7 +67,7 @@
}
return mappingPartitionMetadataCache =
MappingPartitionMetadataInternal.deserialize(
- metadata, fallbackMapVersion, diagnosticsHandler);
+ CompatByteBuffer.wrapOrNull(metadata), fallbackMapVersion, diagnosticsHandler);
}
@Override
@@ -114,7 +115,12 @@
new ByteArrayInputStream(suppliedPartition), alwaysTrue(), true);
classNameMapper =
ClassNameMapper.mapperFromLineReaderWithFiltering(
- reader, metadata.getMapVersion(), diagnosticsHandler, true, allowExperimental)
+ reader,
+ metadata.getMapVersion(),
+ diagnosticsHandler,
+ true,
+ allowExperimental,
+ builder -> builder.setBuildPreamble(true))
.combine(this.classNameMapper);
} catch (IOException e) {
throw new InvalidMappingFileException(e);
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapPartitionerOnClassNameToText.java b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapPartitionerOnClassNameToText.java
index 06ede47..64e4d90 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapPartitionerOnClassNameToText.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapPartitionerOnClassNameToText.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.retrace.ProguardMapPartitioner;
import com.android.tools.r8.retrace.ProguardMapPartitionerBuilder;
import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.RetracePartitionException;
import com.android.tools.r8.retrace.internal.MappingPartitionMetadataInternal.ObfuscatedTypeNameAsKeyMetadata;
import com.android.tools.r8.retrace.internal.MappingPartitionMetadataInternal.ObfuscatedTypeNameAsKeyMetadataWithPartitionNames;
import com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.ProguardMapReaderWithFilteringInputBuffer;
@@ -100,7 +101,8 @@
MapVersion.MAP_VERSION_UNKNOWN,
diagnosticsHandler,
allowEmptyMappedRanges,
- allowExperimentalMapping);
+ allowExperimentalMapping,
+ builder -> builder.setBuildPreamble(true).setAddVersionAsPreamble(true));
reader.forEachClassMapping(
(classMapping, entries) -> {
try {
@@ -161,7 +163,9 @@
} else if (mappingPartitionKeyStrategy
== MappingPartitionKeyStrategy.OBFUSCATED_TYPE_NAME_AS_KEY_WITH_PARTITIONS) {
return ObfuscatedTypeNameAsKeyMetadataWithPartitionNames.create(
- mapVersion, MetadataPartitionCollection.create(keys));
+ mapVersion,
+ MetadataPartitionCollection.create(keys),
+ MetadataAdditionalInfo.create(classMapper.getPreamble()));
} else {
RetracePartitionException retraceError =
new RetracePartitionException("Unknown mapping partitioning strategy");
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierImpl.java
index d791a5c..f2d647a 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMappingSupplierImpl.java
@@ -119,7 +119,12 @@
proguardMapProducer.get(), buildForClass, readPreambleAndSourceFile);
classNameMapper =
ClassNameMapper.mapperFromLineReaderWithFiltering(
- reader, getMapVersion(), diagnosticsHandler, true, allowExperimental)
+ reader,
+ getMapVersion(),
+ diagnosticsHandler,
+ true,
+ allowExperimental,
+ builder -> builder.setBuildPreamble(true))
.combine(classNameMapper);
builtClassMappings.addAll(pendingClassMappings);
pendingClassMappings.clear();
diff --git a/src/main/java/com/android/tools/r8/utils/SerializationUtils.java b/src/main/java/com/android/tools/r8/utils/SerializationUtils.java
new file mode 100644
index 0000000..e400b17
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/SerializationUtils.java
@@ -0,0 +1,26 @@
+// Copyright (c) 2023, 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.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+public class SerializationUtils {
+
+ private static final byte ZERO_BYTE = (byte) 0;
+
+ public static byte getZeroByte() {
+ return ZERO_BYTE;
+ }
+
+ public static void writeUTFOfIntSize(DataOutputStream dataOutputStream, String string)
+ throws IOException {
+ // Similar to dataOutputStream.writeUTF except it uses an int for length in bytes.
+ byte[] bytes = string.getBytes(StandardCharsets.UTF_8);
+ dataOutputStream.writeInt(bytes.length);
+ dataOutputStream.write(bytes);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionAndJoinIdentityTest.java b/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionAndJoinIdentityTest.java
index 83287ff..3836c08 100644
--- a/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionAndJoinIdentityTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionAndJoinIdentityTest.java
@@ -68,24 +68,6 @@
.build()
.run();
List<String> joinedMapLines = StringUtils.splitLines(builder.toString());
- // TODO(b/274735214): Partitioning does not capture the preamble of the mapping file yet, so we
- // discard it before checking equality.
- List<String> lines = Files.readAllLines(mappingFile);
- List<String> filteredLines = lines.subList(computeFirstLine(lines), lines.size());
- assertListsAreEqual(filteredLines, joinedMapLines);
- }
-
- private int computeFirstLine(List<String> lines) {
- int firstLine = 0;
- for (int i = 0; i < lines.size(); i++) {
- String currentLine = lines.get(i).trim();
- if (!currentLine.startsWith("#")
- && currentLine.contains(" -> ")
- && currentLine.endsWith(":")) {
- firstLine = i;
- break;
- }
- }
- return firstLine;
+ assertListsAreEqual(Files.readAllLines(mappingFile), joinedMapLines);
}
}
diff --git a/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionMetadataPartitionNamesTest.java b/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionMetadataPartitionNamesTest.java
index 376d072..2f5bc20 100644
--- a/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionMetadataPartitionNamesTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionMetadataPartitionNamesTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.dex.CompatByteBuffer;
import com.android.tools.r8.naming.MapVersion;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
@@ -82,7 +83,7 @@
byte[] bytes = metadataData.getBytes();
MappingPartitionMetadataInternal mappingPartitionMetadata =
MappingPartitionMetadataInternal.deserialize(
- bytes, MapVersion.MAP_VERSION_NONE, diagnosticsHandler);
+ CompatByteBuffer.wrap(bytes), MapVersion.MAP_VERSION_NONE, diagnosticsHandler);
assertTrue(mappingPartitionMetadata.canGetPartitionKeys());
assertEquals(expectedPartitionKeys, mappingPartitionMetadata.getPartitionKeys());
}
diff --git a/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionMetadataUnknownTest.java b/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionMetadataUnknownTest.java
index 01159fa..a006cab 100644
--- a/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionMetadataUnknownTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionMetadataUnknownTest.java
@@ -11,9 +11,10 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.dex.CompatByteBuffer;
import com.android.tools.r8.naming.MapVersion;
+import com.android.tools.r8.retrace.RetracePartitionException;
import com.android.tools.r8.retrace.internal.MappingPartitionMetadataInternal;
-import com.android.tools.r8.retrace.internal.RetracePartitionException;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import org.junit.Test;
@@ -46,7 +47,7 @@
RetracePartitionException.class,
() ->
MappingPartitionMetadataInternal.deserialize(
- bytes, MapVersion.MAP_VERSION_NONE, diagnosticsHandler));
+ CompatByteBuffer.wrap(bytes), MapVersion.MAP_VERSION_NONE, diagnosticsHandler));
assertEquals(
"Unknown map partition strategy for metadata", retracePartitionException.getMessage());
}