• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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