blob: d634a40bd77b9e108e38d9d3a286f2c2922262ae [file]
// Copyright (c) 2016, 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.dex;
import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.debuginfo.DebugRepresentation;
import com.android.tools.r8.dex.distribution.IndexedItemTransaction;
import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.profile.startup.profile.StartupProfile;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.timing.Timing;
import com.google.common.collect.Sets;
import com.google.common.hash.HashCode;
import com.google.common.hash.Hashing;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class VirtualFile {
public static final int MAX_ENTRIES = Constants.U16BIT_MAX + 1;
private int id;
public final VirtualFileIndexedItemCollection indexedItems;
private final IndexedItemTransaction transaction;
private final FeatureSplit featureSplit;
private final StartupProfile startupProfile;
private final DexString primaryClassDescriptor;
private DebugRepresentation debugRepresentation;
private boolean startup = false;
private HashCode checksumForBuildMetadata;
public VirtualFile(int id, AppView<?> appView) {
this(id, appView, null, null, StartupProfile.empty());
}
public VirtualFile(int id, AppView<?> appView, FeatureSplit featureSplit) {
this(id, appView, null, featureSplit, StartupProfile.empty());
}
public VirtualFile(int id, AppView<?> appView, DexProgramClass primaryClass) {
this(id, appView, primaryClass, null, StartupProfile.empty());
}
public VirtualFile(
int id,
AppView<?> appView,
DexProgramClass primaryClass,
FeatureSplit featureSplit,
StartupProfile startupProfile) {
this.id = id;
this.indexedItems = new VirtualFileIndexedItemCollection();
this.transaction = IndexedItemTransaction.create(indexedItems, appView);
this.featureSplit = featureSplit;
this.startupProfile = startupProfile;
if (primaryClass == null) {
primaryClassDescriptor = null;
} else {
DexType type = primaryClass.getType();
primaryClassDescriptor = appView.getNamingLens().lookupClassDescriptor(type);
}
}
public HashCode getChecksumForBuildMetadata() {
return checksumForBuildMetadata;
}
public VirtualFileIndexedItemCollection getIndexedItems() {
return indexedItems;
}
public IndexedItemTransaction getTransaction() {
return transaction;
}
public void calculateChecksumForBuildMetadata(ByteDataView data, InternalOptions options) {
if (options.r8BuildMetadataConsumer != null) {
checksumForBuildMetadata =
Hashing.sha256()
.hashBytes(data.getBuffer(), data.getOffset(), data.getOffset() + data.getLength());
}
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Set<String> getClassDescriptors() {
Set<String> classDescriptors = new HashSet<>();
for (DexProgramClass clazz : indexedItems.classes) {
boolean added = classDescriptors.add(clazz.type.descriptor.toString());
assert added;
}
return classDescriptors;
}
public FeatureSplit getFeatureSplit() {
return featureSplit;
}
public FeatureSplit getFeatureSplitOrBase() {
return featureSplit != null ? featureSplit : FeatureSplit.BASE;
}
public StartupProfile getStartupProfile() {
return startupProfile;
}
public String getPrimaryClassDescriptor() {
return primaryClassDescriptor == null ? null : primaryClassDescriptor.toString();
}
public void setDebugRepresentation(DebugRepresentation debugRepresentation) {
assert debugRepresentation != null;
assert this.debugRepresentation == null;
this.debugRepresentation = debugRepresentation;
}
public DebugRepresentation getDebugRepresentation() {
assert debugRepresentation != null;
return debugRepresentation;
}
public void setStartup() {
startup = true;
}
public boolean isStartup() {
return startup;
}
public static String deriveCommonPrefixAndSanityCheck(List<String> fileNames) {
Iterator<String> nameIterator = fileNames.iterator();
String first = nameIterator.next();
if (!StringUtils.toLowerCase(first).endsWith(FileUtils.DEX_EXTENSION)) {
throw new RuntimeException("Illegal suffix for dex file: `" + first + "`.");
}
String prefix = first.substring(0, first.length() - FileUtils.DEX_EXTENSION.length());
int index = 2;
while (nameIterator.hasNext()) {
String next = nameIterator.next();
if (!StringUtils.toLowerCase(next).endsWith(FileUtils.DEX_EXTENSION)) {
throw new RuntimeException("Illegal suffix for dex file: `" + first + "`.");
}
if (!next.startsWith(prefix)) {
throw new RuntimeException("Input filenames lack common prefix.");
}
String numberPart =
next.substring(prefix.length(), next.length() - FileUtils.DEX_EXTENSION.length());
if (Integer.parseInt(numberPart) != index++) {
throw new RuntimeException("DEX files are not numbered consecutively.");
}
}
return prefix;
}
private ObjectToOffsetMapping objectMapping = null;
public ObjectToOffsetMapping getObjectMapping() {
assert objectMapping != null;
return objectMapping;
}
public void computeMapping(
AppView<?> appView,
int lazyDexStringsCount,
Timing timing) {
computeMapping(appView, lazyDexStringsCount, timing, null);
}
public void computeMapping(
AppView<?> appView,
int lazyDexStringsCount,
Timing timing,
ObjectToOffsetMapping sharedMapping) {
assert transaction.isEmpty();
assert objectMapping == null;
objectMapping =
new ObjectToOffsetMapping(
appView,
sharedMapping,
transaction.getRewriter(),
indexedItems.classes,
indexedItems.protos.keySet(),
indexedItems.types.keySet(),
indexedItems.methods.keySet(),
indexedItems.fields.keySet(),
indexedItems.strings.keySet(),
indexedItems.callSites.keySet(),
indexedItems.methodHandles.keySet(),
lazyDexStringsCount,
indexedItems.shortyCache,
startupProfile,
this,
timing);
}
public void addClass(DexProgramClass clazz) {
transaction.addClassAndDependencies(clazz);
}
public boolean isFull(int maxEntries) {
return transaction.getNumberOfMethods() > maxEntries
|| transaction.getNumberOfFields() > maxEntries
|| transaction.getNumberOfTypes() > maxEntries;
}
public boolean isFull() {
return isFull(MAX_ENTRIES);
}
public int getNumberOfMethods() {
return transaction.getNumberOfMethods();
}
public int getNumberOfFields() {
return transaction.getNumberOfFields();
}
public int getNumberOfClasses() {
return transaction.getNumberOfClasses();
}
public void throwIfFull(boolean hasMainDexList, Reporter reporter) {
if (!isFull()) {
return;
}
throw reporter.fatalError(
new DexFileOverflowDiagnostic(
hasMainDexList, transaction.getNumberOfMethods(), transaction.getNumberOfFields()));
}
public void abortTransaction() {
transaction.abort();
}
public void commitTransaction() {
transaction.commit();
}
public boolean containsString(DexString string) {
return indexedItems.strings.containsKey(string);
}
public boolean containsType(DexType type) {
return indexedItems.types.containsKey(type);
}
public boolean isEmpty() {
return indexedItems.classes.isEmpty();
}
public Collection<DexProgramClass> classes() {
return indexedItems.classes;
}
public static class VirtualFileIndexedItemCollection {
public final Map<String, DexString> shortyCache = new HashMap<>();
public final Set<DexProgramClass> classes = Sets.newIdentityHashSet();
// Store reference counter for fields, methods, types and strings.
public final Reference2IntMap<DexCallSite> callSites = new Reference2IntOpenHashMap<>();
public final Reference2IntMap<DexField> fields = new Reference2IntOpenHashMap<>();
public final Reference2IntMap<DexMethod> methods = new Reference2IntOpenHashMap<>();
public final Reference2IntMap<DexMethodHandle> methodHandles = new Reference2IntOpenHashMap<>();
public final Reference2IntMap<DexProto> protos = new Reference2IntOpenHashMap<>();
public final Reference2IntMap<DexString> strings = new Reference2IntOpenHashMap<>();
public final Reference2IntMap<DexType> types = new Reference2IntOpenHashMap<>();
public boolean addClass(DexProgramClass clazz) {
return classes.add(clazz);
}
public void addStringsToContainer(Collection<DexString> additionalStrings) {
for (DexString string : additionalStrings) {
strings.put(string, -1);
}
}
public int getNumberOfMethods() {
return methods.size();
}
public int getNumberOfFields() {
return fields.size();
}
public Collection<DexString> getStrings() {
return strings.keySet();
}
}
}