blob: 7e0e22d4fff7415ad2c172d796afdc8e661a65da [file] [log] [blame]
// 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.errors.Unreachable;
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.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
public class MetadataAdditionalInfo {
private static final int NUMBER_OF_ELEMENTS = 2;
public enum AdditionalInfoTypes {
UNKNOWN(-1),
PREAMBLE(0),
OBFUSCATED_PACKAGES(1);
private final int serializedKey;
AdditionalInfoTypes(int serializedKey) {
this.serializedKey = serializedKey;
}
static AdditionalInfoTypes getByKey(int serializedKey) {
if (serializedKey == 0) {
return PREAMBLE;
} else if (serializedKey == 1) {
return OBFUSCATED_PACKAGES;
}
return UNKNOWN;
}
}
protected final List<String> preamble;
protected final Set<String> obfuscatedPackages;
private MetadataAdditionalInfo(List<String> preamble, Set<String> obfuscatedPackages) {
this.preamble = preamble;
this.obfuscatedPackages = obfuscatedPackages;
}
public boolean hasPreamble() {
return preamble != null;
}
public Collection<String> getPreamble() {
return preamble;
}
public boolean hasObfuscatedPackages() {
return obfuscatedPackages != null;
}
public Set<String> getObfuscatedPackages() {
return obfuscatedPackages;
}
// 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(NUMBER_OF_ELEMENTS);
additionalInfoStream.writeShort(AdditionalInfoTypes.PREAMBLE.serializedKey);
SerializationUtils.writeUTFOfIntSize(additionalInfoStream, StringUtils.unixLines(preamble));
additionalInfoStream.writeShort(AdditionalInfoTypes.OBFUSCATED_PACKAGES.serializedKey);
List<String> sortedPackages = new ArrayList<>(obfuscatedPackages);
Collections.sort(sortedPackages);
SerializationUtils.writeUTFOfIntSize(
additionalInfoStream, StringUtils.unixLines(sortedPackages));
byte[] payload = temp.toByteArray();
dataOutputStream.writeInt(payload.length);
dataOutputStream.write(payload);
}
private static MetadataAdditionalInfo deserialize(
byte[] bytes, Predicate<AdditionalInfoTypes> serializeSection) {
CompatByteBuffer compatByteBuffer = CompatByteBuffer.wrap(bytes);
int numberOfElements = compatByteBuffer.getShort();
List<String> preamble = null;
Set<String> packages = 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.UNKNOWN) {
throw new RetracePartitionException(
"Could not additional info from key: " + additionInfoTypeKey);
}
if (serializeSection.test(additionalInfoType)) {
switch (additionalInfoType) {
case PREAMBLE:
preamble = StringUtils.splitLines(compatByteBuffer.getUTFOfIntSize());
break;
case OBFUSCATED_PACKAGES:
packages = StringUtils.splitLinesIntoSet(compatByteBuffer.getUTFOfIntSize());
break;
default:
throw new Unreachable("Unreachable since we already checked for UNKNOWN");
}
} else {
int length = compatByteBuffer.getInt();
compatByteBuffer.position(compatByteBuffer.position() + length);
}
}
return new MetadataAdditionalInfo(preamble, packages);
}
public static MetadataAdditionalInfo create(
List<String> preamble, Set<String> obfuscatedPackages) {
return new MetadataAdditionalInfo(preamble, obfuscatedPackages);
}
public static class LazyMetadataAdditionalInfo extends MetadataAdditionalInfo {
private final byte[] bytes;
private final Map<Integer, MetadataAdditionalInfo> metadataAdditionalInfo =
new ConcurrentHashMap<>();
public LazyMetadataAdditionalInfo(byte[] bytes) {
super(null, null);
this.bytes = bytes;
}
@Override
public boolean hasPreamble() {
MetadataAdditionalInfo metadataAdditionalInfo =
getMetadataAdditionalInfo(AdditionalInfoTypes.PREAMBLE);
return metadataAdditionalInfo != null && metadataAdditionalInfo.hasPreamble();
}
@Override
public Collection<String> getPreamble() {
MetadataAdditionalInfo metadataAdditionalInfo =
getMetadataAdditionalInfo(AdditionalInfoTypes.PREAMBLE);
return metadataAdditionalInfo == null ? null : metadataAdditionalInfo.getPreamble();
}
@Override
public boolean hasObfuscatedPackages() {
MetadataAdditionalInfo metadataAdditionalInfo =
getMetadataAdditionalInfo(AdditionalInfoTypes.OBFUSCATED_PACKAGES);
return metadataAdditionalInfo != null && metadataAdditionalInfo.hasObfuscatedPackages();
}
@Override
public Set<String> getObfuscatedPackages() {
MetadataAdditionalInfo metadataAdditionalInfo =
getMetadataAdditionalInfo(AdditionalInfoTypes.OBFUSCATED_PACKAGES);
return metadataAdditionalInfo == null ? null : metadataAdditionalInfo.getObfuscatedPackages();
}
private MetadataAdditionalInfo getMetadataAdditionalInfo(AdditionalInfoTypes infoType) {
return metadataAdditionalInfo.computeIfAbsent(
infoType.serializedKey,
ignored ->
MetadataAdditionalInfo.deserialize(
bytes, deserializeType -> deserializeType == infoType));
}
public static LazyMetadataAdditionalInfo create(CompatByteBuffer buffer) {
return new LazyMetadataAdditionalInfo(buffer.getBytesOfIntSize());
}
}
}