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