Home | History | Annotate | Download | only in graph
      1 // Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
      2 // for details. All rights reserved. Use of this source code is governed by a
      3 // BSD-style license that can be found in the LICENSE file.
      4 package com.android.tools.r8.graph;
      5 
      6 import com.android.tools.r8.dex.IndexedItemCollection;
      7 import com.android.tools.r8.errors.Unreachable;
      8 import com.android.tools.r8.naming.NamingLens;
      9 import com.android.tools.r8.utils.DescriptorUtils;
     10 import com.google.common.collect.ImmutableSet;
     11 import com.google.common.collect.Iterables;
     12 import com.google.common.collect.Sets;
     13 import java.util.ArrayDeque;
     14 import java.util.Arrays;
     15 import java.util.Deque;
     16 import java.util.Set;
     17 import java.util.function.Consumer;
     18 import java.util.function.Function;
     19 
     20 public class DexType extends IndexedDexItem implements PresortedComparable<DexType> {
     21 
     22   private final static int ROOT_LEVEL = 0;
     23   private final static int UNKNOWN_LEVEL = -1;
     24   private final static int INTERFACE_LEVEL = -2;
     25 
     26   // Since most Java types has no sub-types, we can just share an empty immutable set until we need
     27   // to add to it.
     28   private final static Set<DexType> NO_DIRECT_SUBTYPE = ImmutableSet.of();
     29 
     30   public final DexString descriptor;
     31   private String toStringCache = null;
     32   private int hierarchyLevel = UNKNOWN_LEVEL;
     33   private Set<DexType> directSubtypes = NO_DIRECT_SUBTYPE;
     34 
     35   DexType(DexString descriptor) {
     36     assert !descriptor.toString().contains(".");
     37     this.descriptor = descriptor;
     38   }
     39 
     40   public int computeHashCode() {
     41     return descriptor.hashCode();
     42   }
     43 
     44   public boolean computeEquals(Object other) {
     45     if (other instanceof DexType) {
     46       return descriptor.equals(((DexType) other).descriptor);
     47     }
     48     return false;
     49   }
     50 
     51   private void ensureDirectSubTypeSet() {
     52     if (directSubtypes == NO_DIRECT_SUBTYPE) {
     53       directSubtypes = Sets.newIdentityHashSet();
     54     }
     55   }
     56 
     57   private void setLevel(int level) {
     58     if (level == hierarchyLevel) {
     59       return;
     60     }
     61     if (hierarchyLevel == INTERFACE_LEVEL) {
     62       assert level == ROOT_LEVEL + 1;
     63     } else if (level == INTERFACE_LEVEL) {
     64       assert hierarchyLevel == ROOT_LEVEL + 1 || hierarchyLevel == UNKNOWN_LEVEL;
     65       hierarchyLevel = INTERFACE_LEVEL;
     66     } else {
     67       assert hierarchyLevel == UNKNOWN_LEVEL;
     68       hierarchyLevel = level;
     69     }
     70   }
     71 
     72   public void addDirectSubtype(DexType type) {
     73     assert hierarchyLevel != UNKNOWN_LEVEL;
     74     ensureDirectSubTypeSet();
     75     directSubtypes.add(type);
     76     type.setLevel(hierarchyLevel + 1);
     77   }
     78 
     79   public void tagAsSubtypeRoot() {
     80     setLevel(ROOT_LEVEL);
     81   }
     82 
     83   public boolean isInterface() {
     84     assert isClassType() && hierarchyLevel != UNKNOWN_LEVEL;
     85     return hierarchyLevel == INTERFACE_LEVEL;
     86   }
     87 
     88   public void addInterfaceSubtype(DexType type) {
     89     // Interfaces all inherit from java.lang.Object. However, we assign a special level to
     90     // identify them later on.
     91     setLevel(INTERFACE_LEVEL);
     92     ensureDirectSubTypeSet();
     93     directSubtypes.add(type);
     94   }
     95 
     96   static void clearSubtypeInformation(DexType type) {
     97     type.hierarchyLevel = UNKNOWN_LEVEL;
     98     type.directSubtypes = NO_DIRECT_SUBTYPE;
     99   }
    100 
    101   public boolean isSubtypeOf(DexType other, AppInfo appInfo) {
    102     if (this == other) {
    103       return true;
    104     }
    105     // Treat the object class special as it is always the supertype, even in the case of broken
    106     // subtype chains.
    107     if (this == appInfo.dexItemFactory.objectType) {
    108       return false;
    109     }
    110     if (other == appInfo.dexItemFactory.objectType) {
    111       return true;
    112     }
    113     if (this.hierarchyLevel == INTERFACE_LEVEL) {
    114       return isInterfaceSubtypeOf(this, other, appInfo);
    115     }
    116     if (other.hierarchyLevel == INTERFACE_LEVEL) {
    117       return other.directSubtypes.stream().anyMatch(subtype -> this.isSubtypeOf(subtype,
    118           appInfo));
    119     }
    120     return isSubtypeOfClass(other, appInfo);
    121   }
    122 
    123   private boolean isInterfaceSubtypeOf(DexType candidate, DexType other, AppInfo appInfo) {
    124     if (candidate == other || other == appInfo.dexItemFactory.objectType) {
    125       return true;
    126     }
    127     DexClass candidateHolder = appInfo.definitionFor(candidate);
    128     if (candidateHolder == null) {
    129       return false;
    130     }
    131     for (DexType iface : candidateHolder.interfaces.values) {
    132       assert iface.hierarchyLevel == INTERFACE_LEVEL;
    133       if (isInterfaceSubtypeOf(iface, other, appInfo)) {
    134         return true;
    135       }
    136     }
    137     return false;
    138   }
    139 
    140   private boolean isSubtypeOfClass(DexType other, AppInfo appInfo) {
    141     DexType self = this;
    142     while (other.hierarchyLevel < self.hierarchyLevel) {
    143       DexClass holder = appInfo.definitionFor(self);
    144       assert holder != null && !holder.isInterface();
    145       self = holder.superType;
    146     }
    147     return self == other;
    148   }
    149 
    150   /**
    151    * Apply the given function to all classes that directly extend this class.
    152    *
    153    * If this class is an interface, then this method will visit all sub-interfaces. This deviates
    154    * from the dex-file encoding, where subinterfaces "implement" their super interfaces. However,
    155    * it is consistent with the source language.
    156    */
    157   public void forAllExtendsSubtypes(Consumer<DexType> f) {
    158     assert hierarchyLevel != UNKNOWN_LEVEL;
    159     if (hierarchyLevel == INTERFACE_LEVEL) {
    160       for (DexType subtype : directSubtypes) {
    161         // Other interfaces that extend this interface.
    162         if (subtype.hierarchyLevel == INTERFACE_LEVEL) {
    163           f.accept(subtype);
    164         }
    165       }
    166     } else if (hierarchyLevel == ROOT_LEVEL) {
    167       // This is the object type. Filter out interfaces
    168       for (DexType subtype : directSubtypes) {
    169         // Other interfaces that extend this interface.
    170         if (subtype.hierarchyLevel != INTERFACE_LEVEL) {
    171           f.accept(subtype);
    172         }
    173       }
    174     } else {
    175       directSubtypes.forEach(f);
    176     }
    177   }
    178 
    179   /**
    180    * Apply the given function to all classes that directly implement this interface.
    181    *
    182    * The implementation does not consider how the hierarchy is encoded in the dex file, where
    183    * interfaces "implement" their super interfaces. Instead it takes the view of the source
    184    * language, where interfaces "extend" their superinterface.
    185    */
    186   public void forAllImplementsSubtypes(Consumer<DexType> f) {
    187     if (hierarchyLevel != INTERFACE_LEVEL) {
    188       return;
    189     }
    190     for (DexType subtype : directSubtypes) {
    191       // Filter out other interfaces.
    192       if (subtype.hierarchyLevel != INTERFACE_LEVEL) {
    193         f.accept(subtype);
    194       }
    195     }
    196   }
    197 
    198   public static void forAllInterfaces(DexItemFactory factory, Consumer<DexType> f) {
    199     DexType object = factory.objectType;
    200     assert object.hierarchyLevel == ROOT_LEVEL;
    201     for (DexType subtype : object.directSubtypes) {
    202       if (subtype.isInterface()) {
    203         f.accept(subtype);
    204       }
    205     }
    206   }
    207 
    208   public boolean isSamePackage(DexType other) {
    209     return getPackageDescriptor().equals(other.getPackageDescriptor());
    210   }
    211 
    212   public String toDescriptorString() {
    213     return descriptor.toString();
    214   }
    215 
    216   public String toSourceString() {
    217     if (toStringCache == null) {
    218       // TODO(ager): Pass in a ProguardMapReader to map names back to original names.
    219       if (this == DexItemFactory.catchAllType) {
    220         toStringCache = "CATCH_ALL";
    221       } else {
    222         toStringCache = DescriptorUtils.descriptorToJavaType(toDescriptorString());
    223       }
    224     }
    225     return toStringCache;
    226   }
    227 
    228   public char toShorty() {
    229     char c = (char) descriptor.content[0];
    230     return c == '[' ? 'L' : c;
    231   }
    232 
    233   @Override
    234   public String toSmaliString() {
    235     return toDescriptorString();
    236   }
    237 
    238   @Override
    239   public String toString() {
    240     return toSourceString();
    241   }
    242 
    243   @Override
    244   public void collectIndexedItems(IndexedItemCollection collection) {
    245     if (collection.addType(this)) {
    246       collection.getRenamedDescriptor(this).collectIndexedItems(collection);
    247     }
    248   }
    249 
    250   @Override
    251   public void flushCachedValues() {
    252     super.flushCachedValues();
    253     toStringCache = null;
    254   }
    255 
    256   @Override
    257   public int getOffset(ObjectToOffsetMapping mapping) {
    258     return mapping.getOffsetFor(this);
    259   }
    260 
    261   @Override
    262   public int compareTo(DexType other) {
    263     return sortedCompareTo(other.getSortedIndex());
    264   }
    265 
    266   @Override
    267   public int slowCompareTo(DexType other) {
    268     return descriptor.slowCompareTo(other.descriptor);
    269   }
    270 
    271   @Override
    272   public int slowCompareTo(DexType other, NamingLens namingLens) {
    273     DexString thisDescriptor = namingLens.lookupDescriptor(this);
    274     DexString otherDescriptor = namingLens.lookupDescriptor(other);
    275     return thisDescriptor.slowCompareTo(otherDescriptor);
    276   }
    277 
    278   @Override
    279   public int layeredCompareTo(DexType other, NamingLens namingLens) {
    280     DexString thisDescriptor = namingLens.lookupDescriptor(this);
    281     DexString otherDescriptor = namingLens.lookupDescriptor(other);
    282     return thisDescriptor.compareTo(otherDescriptor);
    283   }
    284 
    285   public boolean isPrimitiveType() {
    286     char firstChar = (char) descriptor.content[0];
    287     return firstChar != 'L' && firstChar != '[';
    288   }
    289 
    290   public boolean isVoidType() {
    291     return (char) descriptor.content[0] == 'V';
    292   }
    293 
    294   public boolean isArrayType() {
    295     char firstChar = (char) descriptor.content[0];
    296     return firstChar == '[';
    297   }
    298 
    299   public boolean isClassType() {
    300     char firstChar = (char) descriptor.content[0];
    301     return firstChar == 'L';
    302   }
    303 
    304   public boolean isPrimitiveArrayType() {
    305     if (!isArrayType()) {
    306       return false;
    307     }
    308     switch (descriptor.content[1]) {
    309       case 'Z':  // boolean
    310       case 'B':  // byte
    311       case 'S':  // short
    312       case 'C':  // char
    313       case 'I':  // int
    314       case 'F':  // float
    315       case 'J':  // long
    316       case 'D':  // double
    317         return true;
    318       default:
    319         return false;
    320     }
    321   }
    322 
    323   public int elementSizeForPrimitiveArrayType() {
    324     assert isPrimitiveArrayType();
    325     switch (descriptor.content[1]) {
    326       case 'Z':  // boolean
    327       case 'B':  // byte
    328         return 1;
    329       case 'S':  // short
    330       case 'C':  // char
    331         return 2;
    332       case 'I':  // int
    333       case 'F':  // float
    334         return 4;
    335       case 'J':  // long
    336       case 'D':  // double
    337         return 8;
    338       default:
    339         throw new Unreachable("Not array of primitives '" + descriptor + "'");
    340     }
    341   }
    342 
    343   private int getNumberOfLeadingSquareBrackets() {
    344     int leadingSquareBrackets = 0;
    345     while (descriptor.content[leadingSquareBrackets] == '[') {
    346       leadingSquareBrackets++;
    347     }
    348     return leadingSquareBrackets;
    349   }
    350 
    351   public DexType toBaseType(DexItemFactory dexItemFactory) {
    352     int leadingSquareBrackets = getNumberOfLeadingSquareBrackets();
    353     if (leadingSquareBrackets == 0) {
    354       return this;
    355     }
    356     DexString newDesc = dexItemFactory.createString(descriptor.size - leadingSquareBrackets,
    357         Arrays.copyOfRange(descriptor.content, leadingSquareBrackets, descriptor.content.length));
    358     return dexItemFactory.createType(newDesc);
    359   }
    360 
    361   public DexType replaceBaseType(DexType newBase, DexItemFactory dexItemFactory) {
    362     assert this.isArrayType();
    363     assert !newBase.isArrayType();
    364     int leadingSquareBrackets = getNumberOfLeadingSquareBrackets();
    365     byte[] content = new byte[newBase.descriptor.content.length + leadingSquareBrackets];
    366     Arrays.fill(content, 0, leadingSquareBrackets, (byte) '[');
    367     for (int i = 0; i < newBase.descriptor.content.length; i++) {
    368       content[leadingSquareBrackets + i] = newBase.descriptor.content[i];
    369     }
    370     DexString newDesc = dexItemFactory
    371         .createString(newBase.descriptor.size + leadingSquareBrackets, content);
    372     return dexItemFactory.createType(newDesc);
    373   }
    374 
    375   public DexType toArrayElementType(DexItemFactory dexItemFactory) {
    376     assert this.isArrayType();
    377     DexString newDesc = dexItemFactory.createString(descriptor.size - 1,
    378         Arrays.copyOfRange(descriptor.content, 1, descriptor.content.length));
    379     return dexItemFactory.createType(newDesc);
    380   }
    381 
    382   static boolean validateLevelsAreCorrect(Function<DexType, DexClass> definitions,
    383       DexItemFactory dexItemFactory) {
    384     Set<DexType> seenTypes = Sets.newIdentityHashSet();
    385     Deque<DexType> worklist = new ArrayDeque<>();
    386     DexType objectType = dexItemFactory.objectType;
    387     worklist.add(objectType);
    388     while (!worklist.isEmpty()) {
    389       DexType next = worklist.pop();
    390       DexClass nextHolder = definitions.apply(next);
    391       DexType superType;
    392       if (nextHolder == null) {
    393         // We might lack the definition of Object, so guard against that.
    394         superType = next == dexItemFactory.objectType ? null : dexItemFactory.objectType;
    395       } else {
    396         superType = nextHolder.superType;
    397       }
    398       assert !seenTypes.contains(next);
    399       seenTypes.add(next);
    400       if (superType == null) {
    401         assert next.hierarchyLevel == ROOT_LEVEL;
    402       } else {
    403         assert superType.hierarchyLevel == next.hierarchyLevel - 1
    404             || superType.hierarchyLevel == ROOT_LEVEL && next.hierarchyLevel == INTERFACE_LEVEL;
    405         assert superType.directSubtypes.contains(next);
    406       }
    407       if (next.hierarchyLevel != INTERFACE_LEVEL) {
    408         // Only traverse the class hierarchy subtypes, not interfaces.
    409         worklist.addAll(next.directSubtypes);
    410       } else if (nextHolder != null) {
    411         // Test that the interfaces of this class are interfaces and have this class as subtype.
    412         for (DexType iface : nextHolder.interfaces.values) {
    413           assert iface.directSubtypes.contains(next);
    414           assert iface.hierarchyLevel == INTERFACE_LEVEL;
    415         }
    416       }
    417     }
    418     return true;
    419   }
    420 
    421   private String getPackageOrName(boolean packagePart) {
    422     assert isClassType();
    423     String descriptor = toDescriptorString();
    424     int lastSeparator = descriptor.lastIndexOf('/');
    425     if (lastSeparator == -1) {
    426       return packagePart ? "" : descriptor.substring(1, descriptor.length() - 1);
    427     } else {
    428       return packagePart ? descriptor.substring(1, lastSeparator)
    429           : descriptor.substring(lastSeparator + 1, descriptor.length() - 1);
    430     }
    431   }
    432 
    433   public DexType getSingleSubtype() {
    434     assert hierarchyLevel != UNKNOWN_LEVEL;
    435     if (directSubtypes.size() == 1) {
    436       return Iterables.getFirst(directSubtypes, null);
    437     } else {
    438       return null;
    439     }
    440   }
    441 
    442   public String getPackageDescriptor() {
    443     return getPackageOrName(true);
    444   }
    445 
    446   public String getName() {
    447     if (isPrimitiveType()) {
    448       return toSourceString();
    449     }
    450     return getPackageOrName(false);
    451   }
    452 }
    453