• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
DexType(DexString descriptor)35   DexType(DexString descriptor) {
36     assert !descriptor.toString().contains(".");
37     this.descriptor = descriptor;
38   }
39 
computeHashCode()40   public int computeHashCode() {
41     return descriptor.hashCode();
42   }
43 
computeEquals(Object other)44   public boolean computeEquals(Object other) {
45     if (other instanceof DexType) {
46       return descriptor.equals(((DexType) other).descriptor);
47     }
48     return false;
49   }
50 
ensureDirectSubTypeSet()51   private void ensureDirectSubTypeSet() {
52     if (directSubtypes == NO_DIRECT_SUBTYPE) {
53       directSubtypes = Sets.newIdentityHashSet();
54     }
55   }
56 
setLevel(int level)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 
addDirectSubtype(DexType type)72   public void addDirectSubtype(DexType type) {
73     assert hierarchyLevel != UNKNOWN_LEVEL;
74     ensureDirectSubTypeSet();
75     directSubtypes.add(type);
76     type.setLevel(hierarchyLevel + 1);
77   }
78 
tagAsSubtypeRoot()79   public void tagAsSubtypeRoot() {
80     setLevel(ROOT_LEVEL);
81   }
82 
isInterface()83   public boolean isInterface() {
84     assert isClassType() && hierarchyLevel != UNKNOWN_LEVEL;
85     return hierarchyLevel == INTERFACE_LEVEL;
86   }
87 
addInterfaceSubtype(DexType type)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 
clearSubtypeInformation(DexType type)96   static void clearSubtypeInformation(DexType type) {
97     type.hierarchyLevel = UNKNOWN_LEVEL;
98     type.directSubtypes = NO_DIRECT_SUBTYPE;
99   }
100 
isSubtypeOf(DexType other, AppInfo appInfo)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 
isInterfaceSubtypeOf(DexType candidate, DexType other, AppInfo appInfo)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 
isSubtypeOfClass(DexType other, AppInfo appInfo)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    */
forAllExtendsSubtypes(Consumer<DexType> f)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    */
forAllImplementsSubtypes(Consumer<DexType> f)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 
forAllInterfaces(DexItemFactory factory, Consumer<DexType> f)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 
isSamePackage(DexType other)208   public boolean isSamePackage(DexType other) {
209     return getPackageDescriptor().equals(other.getPackageDescriptor());
210   }
211 
toDescriptorString()212   public String toDescriptorString() {
213     return descriptor.toString();
214   }
215 
toSourceString()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 
toShorty()228   public char toShorty() {
229     char c = (char) descriptor.content[0];
230     return c == '[' ? 'L' : c;
231   }
232 
233   @Override
toSmaliString()234   public String toSmaliString() {
235     return toDescriptorString();
236   }
237 
238   @Override
toString()239   public String toString() {
240     return toSourceString();
241   }
242 
243   @Override
collectIndexedItems(IndexedItemCollection collection)244   public void collectIndexedItems(IndexedItemCollection collection) {
245     if (collection.addType(this)) {
246       collection.getRenamedDescriptor(this).collectIndexedItems(collection);
247     }
248   }
249 
250   @Override
flushCachedValues()251   public void flushCachedValues() {
252     super.flushCachedValues();
253     toStringCache = null;
254   }
255 
256   @Override
getOffset(ObjectToOffsetMapping mapping)257   public int getOffset(ObjectToOffsetMapping mapping) {
258     return mapping.getOffsetFor(this);
259   }
260 
261   @Override
compareTo(DexType other)262   public int compareTo(DexType other) {
263     return sortedCompareTo(other.getSortedIndex());
264   }
265 
266   @Override
slowCompareTo(DexType other)267   public int slowCompareTo(DexType other) {
268     return descriptor.slowCompareTo(other.descriptor);
269   }
270 
271   @Override
slowCompareTo(DexType other, NamingLens namingLens)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
layeredCompareTo(DexType other, NamingLens namingLens)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 
isPrimitiveType()285   public boolean isPrimitiveType() {
286     char firstChar = (char) descriptor.content[0];
287     return firstChar != 'L' && firstChar != '[';
288   }
289 
isVoidType()290   public boolean isVoidType() {
291     return (char) descriptor.content[0] == 'V';
292   }
293 
isArrayType()294   public boolean isArrayType() {
295     char firstChar = (char) descriptor.content[0];
296     return firstChar == '[';
297   }
298 
isClassType()299   public boolean isClassType() {
300     char firstChar = (char) descriptor.content[0];
301     return firstChar == 'L';
302   }
303 
isPrimitiveArrayType()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 
elementSizeForPrimitiveArrayType()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 
getNumberOfLeadingSquareBrackets()343   private int getNumberOfLeadingSquareBrackets() {
344     int leadingSquareBrackets = 0;
345     while (descriptor.content[leadingSquareBrackets] == '[') {
346       leadingSquareBrackets++;
347     }
348     return leadingSquareBrackets;
349   }
350 
toBaseType(DexItemFactory dexItemFactory)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 
replaceBaseType(DexType newBase, DexItemFactory dexItemFactory)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 
toArrayElementType(DexItemFactory dexItemFactory)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 
validateLevelsAreCorrect(Function<DexType, DexClass> definitions, DexItemFactory dexItemFactory)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 
getPackageOrName(boolean packagePart)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 
getSingleSubtype()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 
getPackageDescriptor()442   public String getPackageDescriptor() {
443     return getPackageOrName(true);
444   }
445 
getName()446   public String getName() {
447     if (isPrimitiveType()) {
448       return toSourceString();
449     }
450     return getPackageOrName(false);
451   }
452 }
453