1 // Copyright (c) 2017, 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.errors.CompilationError; 7 import com.android.tools.r8.ir.code.Invoke.Type; 8 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness; 9 import com.google.common.collect.ImmutableMap; 10 import com.google.common.collect.ImmutableMap.Builder; 11 import java.util.ArrayList; 12 import java.util.List; 13 import java.util.Map; 14 import java.util.concurrent.ConcurrentHashMap; 15 import java.util.function.BiFunction; 16 17 public class AppInfo { 18 19 public final DexApplication app; 20 public final DexItemFactory dexItemFactory; 21 private final ConcurrentHashMap<DexType, Map<Descriptor, KeyedDexItem>> definitions = 22 new ConcurrentHashMap<>(); 23 AppInfo(DexApplication application)24 public AppInfo(DexApplication application) { 25 this.app = application; 26 this.dexItemFactory = app.dexItemFactory; 27 } 28 AppInfo(AppInfo previous)29 protected AppInfo(AppInfo previous) { 30 this.app = previous.app; 31 this.dexItemFactory = app.dexItemFactory; 32 this.definitions.putAll(previous.definitions); 33 } 34 AppInfo(AppInfo previous, GraphLense lense)35 protected AppInfo(AppInfo previous, GraphLense lense) { 36 // Do not rewrite basic structure, as the type information in the lense is about applied uses 37 // and not definitions. 38 this(previous); 39 } 40 computeDefinitions(DexType type)41 private Map<Descriptor, KeyedDexItem> computeDefinitions(DexType type) { 42 Builder<Descriptor, KeyedDexItem> builder = ImmutableMap.builder(); 43 DexClass clazz = app.definitionFor(type); 44 if (clazz != null) { 45 registerDefinitions(builder, clazz.directMethods()); 46 registerDefinitions(builder, clazz.virtualMethods()); 47 registerDefinitions(builder, clazz.instanceFields()); 48 registerDefinitions(builder, clazz.staticFields()); 49 } 50 return builder.build(); 51 } 52 registerDefinitions(Builder<Descriptor, KeyedDexItem> builder, KeyedDexItem<? extends Descriptor>[] items)53 private void registerDefinitions(Builder<Descriptor, KeyedDexItem> builder, 54 KeyedDexItem<? extends Descriptor>[] items) { 55 for (KeyedDexItem<? extends Descriptor> item : items) { 56 builder.put(item.getKey(), item); 57 } 58 } 59 classes()60 public Iterable<DexProgramClass> classes() { 61 return app.classes(); 62 } 63 libraryClasses()64 public Iterable<DexLibraryClass> libraryClasses() { 65 return app.libraryClasses(); 66 } 67 definitionFor(DexType type)68 public DexClass definitionFor(DexType type) { 69 return app.definitionFor(type); 70 } 71 definitionFor(DexMethod method)72 public DexEncodedMethod definitionFor(DexMethod method) { 73 return (DexEncodedMethod) getDefinitions(method.getHolder()).get(method); 74 } 75 definitionFor(DexField field)76 public DexEncodedField definitionFor(DexField field) { 77 return (DexEncodedField) getDefinitions(field.getHolder()).get(field); 78 } 79 getDefinitions(DexType type)80 private Map<Descriptor, KeyedDexItem> getDefinitions(DexType type) { 81 Map<Descriptor, KeyedDexItem> typeDefinitions = definitions.get(type); 82 if (typeDefinitions != null) { 83 return typeDefinitions; 84 } 85 86 typeDefinitions = computeDefinitions(type); 87 Map<Descriptor, KeyedDexItem> existing = definitions.putIfAbsent(type, typeDefinitions); 88 return existing != null ? existing : typeDefinitions; 89 } 90 lookupDirectStaticOrConstructorTarget(DexMethod method)91 private DexEncodedMethod lookupDirectStaticOrConstructorTarget(DexMethod method) { 92 assert method.holder.isClassType(); 93 return lookupTargetAlongSuperChain(method.holder, method, DexClass::findDirectTarget); 94 } 95 96 /** 97 * Lookup static method following the super chain from the holder of {@code method}. 98 * <p> 99 * This method will lookup only static methods. 100 * 101 * @param method the method to lookup 102 * @return The actual target for {@code method} or {@code null} if none found. 103 */ lookupStaticTarget(DexMethod method)104 public DexEncodedMethod lookupStaticTarget(DexMethod method) { 105 DexEncodedMethod target = lookupDirectStaticOrConstructorTarget(method); 106 return target == null || target.accessFlags.isStatic() ? target : null; 107 } 108 109 /** 110 * Lookup direct method following the super chain from the holder of {@code method}. 111 * <p> 112 * This method will lookup private and constructor methods. 113 * 114 * @param method the method to lookup 115 * @return The actual target for {@code method} or {@code null} if none found. 116 */ lookupDirectTarget(DexMethod method)117 public DexEncodedMethod lookupDirectTarget(DexMethod method) { 118 DexEncodedMethod target = lookupDirectStaticOrConstructorTarget(method); 119 return target == null || !target.accessFlags.isStatic() ? target : null; 120 } 121 122 /** 123 * Lookup virtual method starting in type and following the super chain. 124 * <p> 125 * If the target cannot be found along the super-chain, look for a default implementation in one 126 * of the interfaces. 127 */ lookupVirtualTarget(DexType type, DexMethod method)128 public DexEncodedMethod lookupVirtualTarget(DexType type, DexMethod method) { 129 assert type.isClassType(); 130 DexEncodedMethod result 131 = lookupTargetAlongSuperChain(type, method, DexClass::findVirtualTarget); 132 if (result != null) { 133 return result; 134 } 135 return lookupTargetAlongInterfaceChain(type, method, 136 (dexClass, dexMethod) -> { 137 DexEncodedMethod virtualTarget = dexClass.findVirtualTarget(dexMethod); 138 return virtualTarget != null && virtualTarget.getCode() != null 139 ? virtualTarget 140 : null; 141 }); 142 } 143 144 /** 145 * Lookup virtual method starting in type and following the super chain. 146 * <p> 147 * If the target cannot be found along the super-chain, look for a definition in one of 148 * the interfaces. 149 */ lookupVirtualDefinition(DexType type, DexMethod method)150 public DexEncodedMethod lookupVirtualDefinition(DexType type, DexMethod method) { 151 assert type.isClassType(); 152 DexEncodedMethod result 153 = lookupTargetAlongSuperChain(type, method, DexClass::findVirtualTarget); 154 if (result != null) { 155 return result; 156 } 157 return lookupTargetAlongInterfaceChain(type, method, DexClass::findVirtualTarget); 158 } 159 160 /** 161 * Lookup instance field starting in type and following the super chain. 162 */ lookupInstanceTarget(DexType type, DexField field)163 public DexEncodedField lookupInstanceTarget(DexType type, DexField field) { 164 assert type.isClassType(); 165 return lookupTargetAlongSuperChain(type, field, DexClass::findInstanceTarget); 166 } 167 168 /** 169 * Lookup static field starting in type and following the super chain. 170 */ lookupStaticTarget(DexType type, DexField field)171 public DexEncodedField lookupStaticTarget(DexType type, DexField field) { 172 assert type.isClassType(); 173 DexEncodedField target = lookupTargetAlongSuperChain(type, field, DexClass::findStaticTarget); 174 if (target == null) { 175 target = lookupTargetAlongInterfaceChain(type, field, DexClass::findStaticTarget); 176 } 177 return target; 178 } 179 180 /** 181 * Traverse along the super chain until lookup returns non-null value. 182 */ lookupTargetAlongSuperChain( DexType type, T desc, BiFunction<DexClass, T, S> lookup)183 private <S extends DexItem, T extends Descriptor<S, T>> S lookupTargetAlongSuperChain( 184 DexType type, 185 T desc, 186 BiFunction<DexClass, T, S> lookup) { 187 assert type != null; 188 DexClass holder = definitionFor(type); 189 while (holder != null) { 190 S result = lookup.apply(holder, desc); 191 if (result != null) { 192 return result; 193 } 194 if (holder.superType == null) { 195 return null; 196 } 197 holder = definitionFor(holder.superType); 198 } 199 return null; 200 } 201 202 /** 203 * Traverse along the super chain and the interface chains until lookup returns non-null value. 204 */ lookupTargetAlongSuperAndInterfaceChain( DexType type, T desc, BiFunction<DexClass, T, S> lookup)205 private <S extends DexItem, T extends Descriptor<S, T>> S lookupTargetAlongSuperAndInterfaceChain( 206 DexType type, 207 T desc, 208 BiFunction<DexClass, T, S> lookup) { 209 DexClass holder = definitionFor(type); 210 if (holder == null) { 211 return null; 212 } 213 S result = lookup.apply(holder, desc); 214 if (result != null) { 215 return result; 216 } 217 if (holder.superType != null) { 218 result = lookupTargetAlongSuperAndInterfaceChain(holder.superType, desc, lookup); 219 if (result != null) { 220 return result; 221 } 222 } 223 for (DexType iface : holder.interfaces.values) { 224 result = lookupTargetAlongSuperAndInterfaceChain(iface, desc, lookup); 225 if (result != null) { 226 return result; 227 } 228 } 229 return null; 230 } 231 isDefaultMethod(DexItem dexItem)232 private boolean isDefaultMethod(DexItem dexItem) { 233 return dexItem != null && dexItem instanceof DexEncodedMethod && 234 !((DexEncodedMethod) dexItem).accessFlags.isStatic() && 235 ((DexEncodedMethod) dexItem).getCode() != null; 236 } 237 checkIfMethodIsAmbiguous(DexItem previousResult, DexItem newResult)238 private void checkIfMethodIsAmbiguous(DexItem previousResult, DexItem newResult) { 239 if (previousResult != null 240 && previousResult != newResult 241 && isDefaultMethod(previousResult) 242 && isDefaultMethod(newResult)) { 243 throw new CompilationError("Duplicate default methods named " 244 + previousResult.toSourceString() 245 + " are inherited from the types " 246 + ((DexEncodedMethod) previousResult).method.holder.getName() 247 + " and " 248 + ((DexEncodedMethod) newResult).method.holder.getName()); 249 } 250 } 251 252 /** 253 * Traverse along the interface chains until lookup returns non-null value. 254 */ lookupTargetAlongInterfaceChain( DexType type, T desc, BiFunction<DexClass, T, S> lookup)255 private <S extends DexItem, T extends Descriptor<S, T>> S lookupTargetAlongInterfaceChain( 256 DexType type, 257 T desc, 258 BiFunction<DexClass, T, S> lookup) { 259 DexClass holder = definitionFor(type); 260 if (holder == null) { 261 // TODO(herhut): The subtype hierarchy is broken. Handle this case. 262 return null; 263 } 264 S result = null; 265 for (DexType iface : holder.interfaces.values) { 266 S localResult = lookupTargetAlongSuperAndInterfaceChain(iface, desc, lookup); 267 if (localResult != null) { 268 checkIfMethodIsAmbiguous(result, localResult); 269 // Return the first item found, we only continue to detect ambiguous method call. 270 if (result == null) { 271 result = localResult; 272 } 273 } 274 } 275 if (holder.superType != null) { 276 S localResult = lookupTargetAlongInterfaceChain(holder.superType, desc, lookup); 277 if (localResult != null) { 278 checkIfMethodIsAmbiguous(result, localResult); 279 // Return the first item found, we only continue to detect ambiguous method call. 280 if (result == null) { 281 result = localResult; 282 } 283 } 284 } 285 return result; 286 } 287 lookup(Type type, DexMethod target)288 public DexEncodedMethod lookup(Type type, DexMethod target) { 289 DexEncodedMethod definition; 290 DexType holder = target.getHolder(); 291 if (!holder.isClassType()) { 292 return null; 293 } 294 if (type == Type.VIRTUAL || type == Type.INTERFACE) { 295 definition = lookupVirtualDefinition(holder, target); 296 } else if (type == Type.DIRECT) { 297 definition = lookupDirectTarget(target); 298 } else if (type == Type.STATIC) { 299 definition = lookupStaticTarget(target); 300 } else if (type == Type.SUPER) { 301 definition = lookupVirtualTarget(holder, target); 302 } else { 303 return null; 304 } 305 return definition; 306 } 307 hasSubtyping()308 public boolean hasSubtyping() { 309 return false; 310 } 311 withSubtyping()312 public AppInfoWithSubtyping withSubtyping() { 313 return null; 314 } 315 hasLiveness()316 public boolean hasLiveness() { 317 return false; 318 } 319 withLiveness()320 public AppInfoWithLiveness withLiveness() { 321 return null; 322 } 323 getSuperTypeClasses(DexType type)324 public List<DexClass> getSuperTypeClasses(DexType type) { 325 List<DexClass> result = new ArrayList<>(); 326 do { 327 DexClass clazz = definitionFor(type); 328 if (clazz == null) { 329 break; 330 } 331 result.add(clazz); 332 type = clazz.superType; 333 } while (type != null); 334 return result; 335 } 336 } 337