|  | // 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.graph; | 
|  |  | 
|  | import com.android.tools.r8.dex.Constants; | 
|  | import com.android.tools.r8.errors.CompilationError; | 
|  | import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; | 
|  | import com.android.tools.r8.naming.NamingLens; | 
|  | import com.android.tools.r8.utils.Timing; | 
|  | import com.android.tools.r8.utils.structural.CompareToVisitor; | 
|  | import com.android.tools.r8.utils.structural.CompareToVisitorWithStringTable; | 
|  | import com.android.tools.r8.utils.structural.CompareToVisitorWithTypeTable; | 
|  | import com.android.tools.r8.utils.structural.StructuralItem; | 
|  | import it.unimi.dsi.fastutil.objects.Reference2IntLinkedOpenHashMap; | 
|  | import it.unimi.dsi.fastutil.objects.Reference2IntMap; | 
|  | import it.unimi.dsi.fastutil.objects.Reference2IntMap.Entry; | 
|  | import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; | 
|  | import java.util.ArrayList; | 
|  | import java.util.Collection; | 
|  | import java.util.Collections; | 
|  | import java.util.Comparator; | 
|  | import java.util.List; | 
|  | import java.util.function.Consumer; | 
|  | import java.util.stream.Collectors; | 
|  |  | 
|  | public class ObjectToOffsetMapping { | 
|  |  | 
|  | private final static int NOT_FOUND = -1; | 
|  | private final static int NOT_SET = -2; | 
|  |  | 
|  | private final GraphLens graphLens; | 
|  | private final NamingLens namingLens; | 
|  | private final InitClassLens initClassLens; | 
|  | private final LensCodeRewriterUtils lensCodeRewriter; | 
|  |  | 
|  | // Sorted collection of objects mapped to their offsets. | 
|  | private final DexProgramClass[] classes; | 
|  | private final Reference2IntLinkedOpenHashMap<DexProto> protos; | 
|  | private final Reference2IntLinkedOpenHashMap<DexType> types; | 
|  | private final Reference2IntLinkedOpenHashMap<DexMethod> methods; | 
|  | private final Reference2IntLinkedOpenHashMap<DexField> fields; | 
|  | private final Reference2IntLinkedOpenHashMap<DexString> strings; | 
|  | private final Reference2IntLinkedOpenHashMap<DexCallSite> callSites; | 
|  | private final Reference2IntLinkedOpenHashMap<DexMethodHandle> methodHandles; | 
|  |  | 
|  | private DexString firstJumboString; | 
|  |  | 
|  | private final CompareToVisitor compareToVisitor; | 
|  |  | 
|  | public ObjectToOffsetMapping( | 
|  | AppView<?> appView, | 
|  | GraphLens graphLens, | 
|  | NamingLens namingLens, | 
|  | InitClassLens initClassLens, | 
|  | LensCodeRewriterUtils lensCodeRewriter, | 
|  | Collection<DexProgramClass> classes, | 
|  | Collection<DexProto> protos, | 
|  | Collection<DexType> types, | 
|  | Collection<DexMethod> methods, | 
|  | Collection<DexField> fields, | 
|  | Collection<DexString> strings, | 
|  | Collection<DexCallSite> callSites, | 
|  | Collection<DexMethodHandle> methodHandles, | 
|  | Timing timing) { | 
|  | assert appView != null; | 
|  | assert graphLens != null; | 
|  | assert classes != null; | 
|  | assert protos != null; | 
|  | assert types != null; | 
|  | assert methods != null; | 
|  | assert fields != null; | 
|  | assert strings != null; | 
|  | assert callSites != null; | 
|  | assert methodHandles != null; | 
|  | assert initClassLens != null; | 
|  | this.graphLens = graphLens; | 
|  | this.namingLens = namingLens; | 
|  | this.initClassLens = initClassLens; | 
|  | this.lensCodeRewriter = lensCodeRewriter; | 
|  | timing.begin("Sort strings"); | 
|  | this.strings = createSortedMap(strings, DexString::compareTo, this::setFirstJumboString); | 
|  | CompareToVisitor visitor = | 
|  | new CompareToVisitorWithStringTable(namingLens, this.strings::getInt); | 
|  | timing.end(); | 
|  | timing.begin("Sort types"); | 
|  | this.types = createSortedMap(types, compare(visitor), this::failOnOverflow); | 
|  | visitor = | 
|  | new CompareToVisitorWithTypeTable(namingLens, this.strings::getInt, this.types::getInt); | 
|  | timing.end(); | 
|  | timing.begin("Sort classes"); | 
|  | this.classes = sortClasses(appView.appInfo(), classes, namingLens); | 
|  | timing.end(); | 
|  | timing.begin("Sort protos"); | 
|  | this.protos = createSortedMap(protos, compare(visitor), this::failOnOverflow); | 
|  | timing.end(); | 
|  | timing.begin("Sort methods"); | 
|  | this.methods = createSortedMap(methods, compare(visitor), this::failOnOverflow); | 
|  | timing.end(); | 
|  | timing.begin("Sort fields"); | 
|  | this.fields = createSortedMap(fields, compare(visitor), this::failOnOverflow); | 
|  | timing.end(); | 
|  | timing.begin("Sort call-sites"); | 
|  | this.callSites = createSortedMap(callSites, compare(visitor), this::failOnOverflow); | 
|  | timing.end(); | 
|  | timing.begin("Sort method handles"); | 
|  | this.methodHandles = createSortedMap(methodHandles, compare(visitor), this::failOnOverflow); | 
|  | timing.end(); | 
|  |  | 
|  | ObjectToOffsetMapping mapping = this; | 
|  | compareToVisitor = | 
|  | new CompareToVisitorWithTypeTable(namingLens, this.strings::getInt, this.types::getInt) { | 
|  |  | 
|  | @Override | 
|  | public int visitDexField(DexField field1, DexField field2) { | 
|  | return Integer.compare(mapping.fields.getInt(field1), mapping.fields.getInt(field2)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int visitDexMethod(DexMethod method1, DexMethod method2) { | 
|  | return Integer.compare( | 
|  | mapping.methods.getInt(method1), mapping.methods.getInt(method2)); | 
|  | } | 
|  | }; | 
|  | } | 
|  |  | 
|  | public CompareToVisitor getCompareToVisitor() { | 
|  | return compareToVisitor; | 
|  | } | 
|  |  | 
|  | private <T extends StructuralItem<T>> Comparator<T> compare(CompareToVisitor visitor) { | 
|  | return (a, b) -> a.acceptCompareTo(b, visitor); | 
|  | } | 
|  |  | 
|  | private void setFirstJumboString(DexString string) { | 
|  | assert firstJumboString == null; | 
|  | firstJumboString = string; | 
|  | } | 
|  |  | 
|  | private void failOnOverflow(DexItem item) { | 
|  | throw new CompilationError("Index overflow for " + item.getClass()); | 
|  | } | 
|  |  | 
|  | private <T> Reference2IntLinkedOpenHashMap<T> createSortedMap( | 
|  | Collection<T> items, Comparator<T> comparator, Consumer<T> onUInt16Overflow) { | 
|  | if (items.isEmpty()) { | 
|  | return new Reference2IntLinkedOpenHashMap<>(); | 
|  | } | 
|  | // Sort items and compute the offset mapping for each in sorted order. | 
|  | ArrayList<T> sorted = new ArrayList<>(items); | 
|  | sorted.sort(comparator); | 
|  | Reference2IntLinkedOpenHashMap<T> map = new Reference2IntLinkedOpenHashMap<>(items.size()); | 
|  | map.defaultReturnValue(NOT_FOUND); | 
|  | int index = 0; | 
|  | for (T item : sorted) { | 
|  | if (index == Constants.U16BIT_MAX + 1) { | 
|  | onUInt16Overflow.accept(item); | 
|  | } | 
|  | map.put(item, index++); | 
|  | } | 
|  | return map; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Here, 'depth' of a program class is an integer one bigger then the maximum depth of its | 
|  | * superclass and implemented interfaces. The depth of classes without any or without known | 
|  | * superclasses and interfaces is 1. | 
|  | */ | 
|  | private static class ProgramClassDepthsMemoized { | 
|  |  | 
|  | private final static int UNKNOWN_DEPTH = -1; | 
|  |  | 
|  | private final AppInfo appInfo; | 
|  | private final Reference2IntMap<DexProgramClass> depthOfClasses = new Reference2IntOpenHashMap<>(); | 
|  |  | 
|  | ProgramClassDepthsMemoized(AppInfo appInfo) { | 
|  | this.appInfo = appInfo; | 
|  | depthOfClasses.defaultReturnValue(UNKNOWN_DEPTH); | 
|  | } | 
|  |  | 
|  | int getDepth(DexProgramClass programClass) { | 
|  | int depth = depthOfClasses.getInt(programClass); | 
|  |  | 
|  | // TODO(b/65536002): use "computeIntIfAbsent" after upgrading to fastutils 8.x. | 
|  | if (depth == UNKNOWN_DEPTH) { | 
|  | // Emulating the algorithm of com.android.dx.merge.SortableType.tryAssignDepth(). | 
|  | DexType superType = programClass.superType; | 
|  | int maxDepth; | 
|  | if (superType == null) { | 
|  | maxDepth = 0; | 
|  | } else { | 
|  | maxDepth = 1; | 
|  | DexProgramClass superClass = appInfo.programDefinitionFor(superType, programClass); | 
|  | if (superClass != null) { | 
|  | maxDepth = getDepth(superClass); | 
|  | } | 
|  | } | 
|  | for (DexType inf : programClass.interfaces.values) { | 
|  | DexProgramClass infClass = appInfo.programDefinitionFor(inf, programClass); | 
|  | maxDepth = Math.max(maxDepth, infClass == null ? 1 : getDepth(infClass)); | 
|  | } | 
|  | depth = maxDepth + 1; | 
|  | depthOfClasses.put(programClass, depth); | 
|  | } | 
|  |  | 
|  | return depth; | 
|  | } | 
|  | } | 
|  |  | 
|  | private static DexProgramClass[] sortClasses( | 
|  | AppInfo appInfo, Collection<DexProgramClass> classes, NamingLens namingLens) { | 
|  | // Collect classes in subtyping order, based on a sorted list of classes to start with. | 
|  | ProgramClassDepthsMemoized classDepths = new ProgramClassDepthsMemoized(appInfo); | 
|  | List<DexProgramClass> sortedClasses = | 
|  | classes.stream() | 
|  | .sorted( | 
|  | (x, y) -> { | 
|  | int dx = classDepths.getDepth(x); | 
|  | int dy = classDepths.getDepth(y); | 
|  | return dx != dy ? dx - dy : x.type.compareToWithNamingLens(y.type, namingLens); | 
|  | }) | 
|  | .collect(Collectors.toList()); | 
|  | return sortedClasses.toArray(DexProgramClass.EMPTY_ARRAY); | 
|  | } | 
|  |  | 
|  | private static <T> Collection<T> keysOrEmpty(Reference2IntLinkedOpenHashMap<T> map) { | 
|  | // The key-set is deterministic (linked) and inserted in sorted order. | 
|  | return map == null ? Collections.emptyList() : map.keySet(); | 
|  | } | 
|  |  | 
|  | public GraphLens getGraphLens() { | 
|  | return graphLens; | 
|  | } | 
|  |  | 
|  | public NamingLens getNamingLens() { | 
|  | return namingLens; | 
|  | } | 
|  |  | 
|  | public LensCodeRewriterUtils getLensCodeRewriter() { | 
|  | return lensCodeRewriter; | 
|  | } | 
|  |  | 
|  | public Collection<DexMethod> getMethods() { | 
|  | return keysOrEmpty(methods); | 
|  | } | 
|  |  | 
|  | public DexProgramClass[] getClasses() { | 
|  | return classes; | 
|  | } | 
|  |  | 
|  | public Collection<DexType> getTypes() { | 
|  | return keysOrEmpty(types); | 
|  | } | 
|  |  | 
|  | public Collection<DexProto> getProtos() { | 
|  | return keysOrEmpty(protos); | 
|  | } | 
|  |  | 
|  | public Collection<DexField> getFields() { | 
|  | return keysOrEmpty(fields); | 
|  | } | 
|  |  | 
|  | public Collection<DexString> getStrings() { | 
|  | return keysOrEmpty(strings); | 
|  | } | 
|  |  | 
|  | public Collection<DexCallSite> getCallSites() { | 
|  | return keysOrEmpty(callSites); | 
|  | } | 
|  |  | 
|  | public Collection<DexMethodHandle> getMethodHandles() { | 
|  | return keysOrEmpty(methodHandles); | 
|  | } | 
|  |  | 
|  | public boolean hasJumboStrings() { | 
|  | return firstJumboString != null; | 
|  | } | 
|  |  | 
|  | public DexString getFirstJumboString() { | 
|  | return firstJumboString; | 
|  | } | 
|  |  | 
|  | public DexString getFirstString() { | 
|  | for (Entry<DexString> dexStringEntry : strings.reference2IntEntrySet()) { | 
|  | if (dexStringEntry.getIntValue() == 0) { | 
|  | return dexStringEntry.getKey(); | 
|  | } | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | private <T extends IndexedDexItem> int getOffsetFor(T item, Reference2IntMap<T> map) { | 
|  | int index = map.getInt(item); | 
|  | assert index != NOT_SET : "Index was not set: " + item; | 
|  | assert index != NOT_FOUND : "Missing dependency: " + item; | 
|  | return index; | 
|  | } | 
|  |  | 
|  | public int getOffsetFor(DexProto proto) { | 
|  | return getOffsetFor(proto, protos); | 
|  | } | 
|  |  | 
|  | public int getOffsetFor(DexField field) { | 
|  | return getOffsetFor(field, fields); | 
|  | } | 
|  |  | 
|  | public int getOffsetFor(DexMethod method) { | 
|  | return getOffsetFor(method, methods); | 
|  | } | 
|  |  | 
|  | public int getOffsetFor(DexString string) { | 
|  | return getOffsetFor(string, strings); | 
|  | } | 
|  |  | 
|  | public int getOffsetFor(DexType type) { | 
|  | return getOffsetFor(type, types); | 
|  | } | 
|  |  | 
|  | public int getOffsetFor(DexCallSite callSite) { | 
|  | return getOffsetFor(callSite, callSites); | 
|  | } | 
|  |  | 
|  | public int getOffsetFor(DexMethodHandle methodHandle) { | 
|  | return getOffsetFor(methodHandle, methodHandles); | 
|  | } | 
|  |  | 
|  | public DexField getClinitField(DexType type) { | 
|  | return initClassLens.getInitClassField(type); | 
|  | } | 
|  | } |