• 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.utils;
5 
6 import com.android.tools.r8.code.Iget;
7 import com.android.tools.r8.code.IgetBoolean;
8 import com.android.tools.r8.code.IgetByte;
9 import com.android.tools.r8.code.IgetChar;
10 import com.android.tools.r8.code.IgetObject;
11 import com.android.tools.r8.code.IgetShort;
12 import com.android.tools.r8.code.IgetWide;
13 import com.android.tools.r8.code.Instruction;
14 import com.android.tools.r8.code.InvokeDirect;
15 import com.android.tools.r8.code.InvokeDirectRange;
16 import com.android.tools.r8.code.InvokeInterface;
17 import com.android.tools.r8.code.InvokeInterfaceRange;
18 import com.android.tools.r8.code.InvokeStatic;
19 import com.android.tools.r8.code.InvokeStaticRange;
20 import com.android.tools.r8.code.InvokeSuper;
21 import com.android.tools.r8.code.InvokeSuperRange;
22 import com.android.tools.r8.code.InvokeVirtual;
23 import com.android.tools.r8.code.InvokeVirtualRange;
24 import com.android.tools.r8.code.Iput;
25 import com.android.tools.r8.code.IputBoolean;
26 import com.android.tools.r8.code.IputByte;
27 import com.android.tools.r8.code.IputChar;
28 import com.android.tools.r8.code.IputObject;
29 import com.android.tools.r8.code.IputShort;
30 import com.android.tools.r8.code.IputWide;
31 import com.android.tools.r8.code.Sget;
32 import com.android.tools.r8.code.SgetBoolean;
33 import com.android.tools.r8.code.SgetByte;
34 import com.android.tools.r8.code.SgetChar;
35 import com.android.tools.r8.code.SgetObject;
36 import com.android.tools.r8.code.SgetShort;
37 import com.android.tools.r8.code.SgetWide;
38 import com.android.tools.r8.code.Sput;
39 import com.android.tools.r8.code.SputBoolean;
40 import com.android.tools.r8.code.SputByte;
41 import com.android.tools.r8.code.SputChar;
42 import com.android.tools.r8.code.SputObject;
43 import com.android.tools.r8.code.SputShort;
44 import com.android.tools.r8.code.SputWide;
45 import com.android.tools.r8.dex.ApplicationReader;
46 import com.android.tools.r8.graph.DexAccessFlags;
47 import com.android.tools.r8.graph.DexAnnotation;
48 import com.android.tools.r8.graph.DexApplication;
49 import com.android.tools.r8.graph.DexClass;
50 import com.android.tools.r8.graph.DexCode;
51 import com.android.tools.r8.graph.DexEncodedAnnotation;
52 import com.android.tools.r8.graph.DexEncodedField;
53 import com.android.tools.r8.graph.DexEncodedMethod;
54 import com.android.tools.r8.graph.DexField;
55 import com.android.tools.r8.graph.DexItemFactory;
56 import com.android.tools.r8.graph.DexMethod;
57 import com.android.tools.r8.graph.DexProto;
58 import com.android.tools.r8.graph.DexType;
59 import com.android.tools.r8.naming.ClassNameMapper;
60 import com.android.tools.r8.naming.ClassNaming;
61 import com.android.tools.r8.naming.MemberNaming;
62 import com.android.tools.r8.naming.MemberNaming.FieldSignature;
63 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
64 import com.android.tools.r8.naming.MemberNaming.Signature;
65 import com.android.tools.r8.naming.ProguardMapReader;
66 import com.google.common.collect.BiMap;
67 import com.google.common.collect.ImmutableList;
68 import java.io.IOException;
69 import java.lang.reflect.Method;
70 import java.nio.file.Path;
71 import java.nio.file.Paths;
72 import java.util.ArrayList;
73 import java.util.Collections;
74 import java.util.Iterator;
75 import java.util.List;
76 import java.util.NoSuchElementException;
77 import java.util.concurrent.ExecutionException;
78 import java.util.concurrent.ExecutorService;
79 import java.util.concurrent.Executors;
80 import java.util.function.BiFunction;
81 import java.util.function.Consumer;
82 import java.util.function.Function;
83 import java.util.function.Predicate;
84 
85 public class DexInspector {
86 
87   private final DexApplication application;
88   private final DexItemFactory dexItemFactory;
89   private final ClassNameMapper mapping;
90   private final BiMap<String, String> originalToObfuscatedMapping;
91 
92   private final InstructionSubjectFactory factory = new InstructionSubjectFactory();
93 
94   public static MethodSignature MAIN =
95       new MethodSignature("main", "void", new String[]{"java.lang.String[]"});
96 
DexInspector(Path file, String mappingFile)97   public DexInspector(Path file, String mappingFile) throws IOException, ExecutionException {
98     this(Collections.singletonList(file), mappingFile);
99   }
100 
DexInspector(Path file)101   public DexInspector(Path file) throws IOException, ExecutionException {
102     this(Collections.singletonList(file), null);
103   }
104 
DexInspector(List<Path> files)105   public DexInspector(List<Path> files) throws IOException, ExecutionException {
106     this(files, null);
107   }
108 
DexInspector(List<Path> files, String mappingFile)109   public DexInspector(List<Path> files, String mappingFile)
110       throws IOException, ExecutionException {
111     ExecutorService executor = Executors.newSingleThreadExecutor();
112     if (mappingFile != null) {
113       this.mapping = ProguardMapReader.mapperFromFile(Paths.get(mappingFile));
114       originalToObfuscatedMapping = this.mapping.getObfuscatedToOriginalMapping().inverse();
115     } else {
116       this.mapping = null;
117       originalToObfuscatedMapping = null;
118     }
119     Timing timing = new Timing("DexInspector");
120     InternalOptions options = new InternalOptions();
121     dexItemFactory = options.itemFactory;
122     AndroidApp input = AndroidApp.fromProgramFiles(files);
123     application = new ApplicationReader(input, options, timing).read(executor);
124     executor.shutdown();
125   }
126 
DexInspector(AndroidApp app)127   public DexInspector(AndroidApp app) throws IOException, ExecutionException {
128     this(new ApplicationReader(app, new InternalOptions(), new Timing("DexInspector")).read());
129   }
130 
DexInspector(DexApplication application)131   public DexInspector(DexApplication application) {
132     dexItemFactory = application.dexItemFactory;
133     this.application = application;
134     this.mapping = application.getProguardMap();
135     originalToObfuscatedMapping =
136         mapping == null ? null : mapping.getObfuscatedToOriginalMapping().inverse();
137   }
138 
getFactory()139   public DexItemFactory getFactory() {
140     return dexItemFactory;
141   }
142 
toDexType(String string)143   private DexType toDexType(String string) {
144     return dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(string));
145   }
146 
forAll(S[] items, BiFunction<S, FoundClassSubject, ? extends T> constructor, FoundClassSubject clazz, Consumer<T> consumer)147   private static <S, T extends Subject> void forAll(S[] items,
148       BiFunction<S, FoundClassSubject, ? extends T> constructor,
149       FoundClassSubject clazz,
150       Consumer<T> consumer) {
151     for (S item : items) {
152       consumer.accept(constructor.apply(item, clazz));
153     }
154   }
155 
forAll(Iterable<S> items, Function<S, T> constructor, Consumer<T> consumer)156   private static <S, T extends Subject> void forAll(Iterable<S> items, Function<S, T> constructor,
157       Consumer<T> consumer) {
158     for (S item : items) {
159       consumer.accept(constructor.apply(item));
160     }
161   }
162 
clazz(Class clazz)163   public ClassSubject clazz(Class clazz) {
164     return clazz(clazz.getTypeName());
165   }
166 
clazz(String name)167   public ClassSubject clazz(String name) {
168     ClassNaming naming = null;
169     if (mapping != null) {
170       String obfuscated = originalToObfuscatedMapping.get(name);
171       if (obfuscated != null) {
172         naming = mapping.getClassNaming(obfuscated);
173         name = obfuscated;
174       }
175     }
176     DexClass clazz = application.definitionFor(toDexType(name));
177     if (clazz == null) {
178       return new AbsentClassSubject();
179     }
180     return new FoundClassSubject(clazz, naming);
181   }
182 
forAllClasses(Consumer<FoundClassSubject> inspection)183   public void forAllClasses(Consumer<FoundClassSubject> inspection) {
184     forAll(application.classes(), clazz -> {
185       ClassNaming naming = null;
186       if (mapping != null) {
187         String obfuscated = originalToObfuscatedMapping.get(clazz.type.toSourceString());
188         if (obfuscated != null) {
189           naming = mapping.getClassNaming(obfuscated);
190         }
191       }
192       return new FoundClassSubject(clazz, naming);
193     }, inspection);
194   }
195 
method(Method method)196   public MethodSubject method(Method method) {
197     ClassSubject clazz = clazz(method.getDeclaringClass());
198     if (!clazz.isPresent()) {
199       return new AbsentMethodSubject();
200     }
201     return clazz.method(method);
202   }
203 
getObfuscatedTypeName(String originalTypeName)204   private String getObfuscatedTypeName(String originalTypeName) {
205     String obfuscatedType = null;
206     if (mapping != null) {
207       obfuscatedType = originalToObfuscatedMapping.get(originalTypeName);
208     }
209     obfuscatedType = obfuscatedType == null ? originalTypeName : obfuscatedType;
210     return obfuscatedType;
211   }
212 
213   public abstract class Subject {
214 
isPresent()215     public abstract boolean isPresent();
216   }
217 
218   public abstract class AnnotationSubject extends Subject {
219 
getAnnotation()220     public abstract DexEncodedAnnotation getAnnotation();
221   }
222 
223   public class FoundAnnotationSubject extends AnnotationSubject {
224 
225     private final DexAnnotation annotation;
226 
FoundAnnotationSubject(DexAnnotation annotation)227     private FoundAnnotationSubject(DexAnnotation annotation) {
228       this.annotation = annotation;
229     }
230 
231     @Override
isPresent()232     public boolean isPresent() {
233       return true;
234     }
235 
236     @Override
getAnnotation()237     public DexEncodedAnnotation getAnnotation() {
238       return annotation.annotation;
239     }
240   }
241 
242   public class AbsentAnnotationSubject extends AnnotationSubject {
243 
244     @Override
isPresent()245     public boolean isPresent() {
246       return false;
247     }
248 
249     @Override
getAnnotation()250     public DexEncodedAnnotation getAnnotation() {
251       throw new UnsupportedOperationException();
252     }
253   }
254 
255 
256   public abstract class ClassSubject extends Subject {
257 
forAllMethods(Consumer<FoundMethodSubject> inspection)258     public abstract void forAllMethods(Consumer<FoundMethodSubject> inspection);
259 
method(Method method)260     public MethodSubject method(Method method) {
261       List<String> parameters = new ArrayList<>();
262       for (Class<?> parameterType : method.getParameterTypes()) {
263         parameters.add(parameterType.getTypeName());
264       }
265       return method(method.getReturnType().getTypeName(), method.getName(), parameters);
266     }
267 
method(String returnType, String name, List<String> parameters)268     public abstract MethodSubject method(String returnType, String name, List<String> parameters);
269 
method(MethodSignature signature)270     public MethodSubject method(MethodSignature signature) {
271       return method(signature.type, signature.name, ImmutableList.copyOf(signature.parameters));
272     }
273 
forAllFields(Consumer<FoundFieldSubject> inspection)274     public abstract void forAllFields(Consumer<FoundFieldSubject> inspection);
275 
field(String type, String name)276     public abstract FieldSubject field(String type, String name);
277 
isAbstract()278     public abstract boolean isAbstract();
279 
dumpMethods()280     public String dumpMethods() {
281       StringBuilder dump = new StringBuilder();
282       forAllMethods((FoundMethodSubject method) ->
283           dump.append(method.getMethod().toString())
284               .append(method.getMethod().codeToString()));
285       return dump.toString();
286     }
287 
getDexClass()288     public abstract DexClass getDexClass();
289 
annotation(String name)290     public abstract AnnotationSubject annotation(String name);
291 
getOriginalDescriptor()292     public abstract String getOriginalDescriptor();
293 
getFinalDescriptor()294     public abstract String getFinalDescriptor();
295 
isRenamed()296     public abstract boolean isRenamed();
297   }
298 
299   private class AbsentClassSubject extends ClassSubject {
300 
301     @Override
isPresent()302     public boolean isPresent() {
303       return false;
304     }
305 
306     @Override
forAllMethods(Consumer<FoundMethodSubject> inspection)307     public void forAllMethods(Consumer<FoundMethodSubject> inspection) {
308     }
309 
310     @Override
method(String returnType, String name, List<String> parameters)311     public MethodSubject method(String returnType, String name, List<String> parameters) {
312       return new AbsentMethodSubject();
313     }
314 
315     @Override
forAllFields(Consumer<FoundFieldSubject> inspection)316     public void forAllFields(Consumer<FoundFieldSubject> inspection) {
317     }
318 
319     @Override
field(String type, String name)320     public FieldSubject field(String type, String name) {
321       return new AbsentFieldSubject();
322     }
323 
324     @Override
isAbstract()325     public boolean isAbstract() {
326       return false;
327     }
328 
329     @Override
getDexClass()330     public DexClass getDexClass() {
331       return null;
332     }
333 
334     @Override
annotation(String name)335     public AnnotationSubject annotation(String name) {
336       return new AbsentAnnotationSubject();
337     }
338 
339     @Override
getOriginalDescriptor()340     public String getOriginalDescriptor() {
341       return null;
342     }
343 
344     @Override
getFinalDescriptor()345     public String getFinalDescriptor() {
346       return null;
347     }
348 
349     @Override
isRenamed()350     public boolean isRenamed() {
351       return false;
352     }
353   }
354 
355   public class FoundClassSubject extends ClassSubject {
356 
357     private final DexClass dexClass;
358     private final ClassNaming naming;
359 
FoundClassSubject(DexClass dexClass, ClassNaming naming)360     private FoundClassSubject(DexClass dexClass, ClassNaming naming) {
361       this.dexClass = dexClass;
362       this.naming = naming;
363     }
364 
365     @Override
isPresent()366     public boolean isPresent() {
367       return true;
368     }
369 
370     @Override
forAllMethods(Consumer<FoundMethodSubject> inspection)371     public void forAllMethods(Consumer<FoundMethodSubject> inspection) {
372       forAll(dexClass.directMethods(), FoundMethodSubject::new, this, inspection);
373       forAll(dexClass.virtualMethods(), FoundMethodSubject::new, this, inspection);
374     }
375 
376     @Override
method(String returnType, String name, List<String> parameters)377     public MethodSubject method(String returnType, String name, List<String> parameters) {
378       DexType[] parameterTypes = new DexType[parameters.size()];
379       for (int i = 0; i < parameters.size(); i++) {
380         parameterTypes[i] = toDexType(getObfuscatedTypeName(parameters.get(i)));
381       }
382       DexProto proto = dexItemFactory.createProto(toDexType(getObfuscatedTypeName(returnType)),
383           parameterTypes);
384       if (naming != null) {
385         String[] parameterStrings = new String[parameterTypes.length];
386         Signature signature = new MethodSignature(name, returnType,
387             parameters.toArray(parameterStrings));
388         MemberNaming methodNaming = naming.lookupByOriginalSignature(signature);
389         if (methodNaming != null) {
390           name = methodNaming.getRenamedName();
391         }
392       }
393       DexMethod dexMethod =
394           dexItemFactory.createMethod(dexClass.type, proto, dexItemFactory.createString(name));
395       DexEncodedMethod encoded = findMethod(dexClass.directMethods(), dexMethod);
396       if (encoded == null) {
397         encoded = findMethod(dexClass.virtualMethods(), dexMethod);
398       }
399       return encoded == null ? new AbsentMethodSubject() : new FoundMethodSubject(encoded, this);
400     }
401 
findMethod(DexEncodedMethod[] methods, DexMethod dexMethod)402     private DexEncodedMethod findMethod(DexEncodedMethod[] methods, DexMethod dexMethod) {
403       for (DexEncodedMethod method : methods) {
404         if (method.method.equals(dexMethod)) {
405           return method;
406         }
407       }
408       return null;
409     }
410 
411     @Override
forAllFields(Consumer<FoundFieldSubject> inspection)412     public void forAllFields(Consumer<FoundFieldSubject> inspection) {
413       forAll(dexClass.staticFields(), FoundFieldSubject::new, this, inspection);
414       forAll(dexClass.instanceFields(), FoundFieldSubject::new, this, inspection);
415     }
416 
417     @Override
field(String type, String name)418     public FieldSubject field(String type, String name) {
419       String obfuscatedType = getObfuscatedTypeName(type);
420       MemberNaming fieldNaming = null;
421       if (naming != null) {
422         fieldNaming = naming.lookupByOriginalSignature(
423             new FieldSignature(name, type));
424       }
425       String obfuscatedName = fieldNaming == null ? name : fieldNaming.getRenamedName();
426 
427       DexField field = dexItemFactory.createField(dexClass.type,
428           toDexType(obfuscatedType), dexItemFactory.createString(obfuscatedName));
429       DexEncodedField encoded = findField(dexClass.staticFields(), field);
430       if (encoded == null) {
431         encoded = findField(dexClass.instanceFields(), field);
432       }
433       return encoded == null ? new AbsentFieldSubject() : new FoundFieldSubject(encoded, this);
434     }
435 
436     @Override
isAbstract()437     public boolean isAbstract() {
438       return dexClass.accessFlags.isAbstract();
439     }
440 
findField(DexEncodedField[] fields, DexField dexField)441     private DexEncodedField findField(DexEncodedField[] fields, DexField dexField) {
442       for (DexEncodedField field : fields) {
443         if (field.field.equals(dexField)) {
444           return field;
445         }
446       }
447       return null;
448     }
449 
450     @Override
getDexClass()451     public DexClass getDexClass() {
452       return dexClass;
453     }
454 
455     @Override
annotation(String name)456     public AnnotationSubject annotation(String name) {
457       DexAnnotation annotation = findAnnotation(name);
458       return annotation == null
459           ? new AbsentAnnotationSubject()
460           : new FoundAnnotationSubject(annotation);
461     }
462 
findAnnotation(String name)463     private DexAnnotation findAnnotation(String name) {
464       for (DexAnnotation annotation : dexClass.annotations.annotations) {
465         DexType type = annotation.annotation.type;
466         String original = mapping == null ? type.toSourceString() : mapping.originalNameOf(type);
467         if (original.equals(name)) {
468           return annotation;
469         }
470       }
471       return null;
472     }
473 
474     @Override
getOriginalDescriptor()475     public String getOriginalDescriptor() {
476       if (naming != null) {
477         return DescriptorUtils.javaTypeToDescriptor(naming.originalName);
478       } else {
479         return getFinalDescriptor();
480       }
481     }
482 
483     @Override
getFinalDescriptor()484     public String getFinalDescriptor() {
485       return dexClass.type.descriptor.toString();
486     }
487 
488     @Override
isRenamed()489     public boolean isRenamed() {
490       return naming == null || !getFinalDescriptor().equals(getOriginalDescriptor());
491     }
492 
493     @Override
toString()494     public String toString() {
495       return dexClass.toSourceString();
496     }
497   }
498 
499   public abstract class MemberSubject extends Subject {
500 
hasAll(DexAccessFlags flags)501     public abstract boolean hasAll(DexAccessFlags flags);
502 
hasNone(DexAccessFlags flags)503     public abstract boolean hasNone(DexAccessFlags flags);
504 
isStatic()505     public abstract boolean isStatic();
506 
isFinal()507     public abstract boolean isFinal();
508 
getOriginalSignature()509     public abstract Signature getOriginalSignature();
510 
getFinalSignature()511     public abstract Signature getFinalSignature();
512   }
513 
514   public abstract class MethodSubject extends MemberSubject {
515 
isAbstract()516     public abstract boolean isAbstract();
517 
isBridge()518     public abstract boolean isBridge();
519 
getMethod()520     public abstract DexEncodedMethod getMethod();
521 
iterateInstructions()522     public Iterator<InstructionSubject> iterateInstructions() {
523       return null;
524     }
525 
iterateInstructions( Predicate<InstructionSubject> filter)526     public <T extends InstructionSubject> Iterator<T> iterateInstructions(
527         Predicate<InstructionSubject> filter) {
528       return null;
529     }
530 
isRenamed()531     public abstract boolean isRenamed();
532   }
533 
534   public class AbsentMethodSubject extends MethodSubject {
535 
536     @Override
isPresent()537     public boolean isPresent() {
538       return false;
539     }
540 
541     @Override
isRenamed()542     public boolean isRenamed() {
543       return false;
544     }
545 
546     @Override
hasAll(DexAccessFlags flags)547     public boolean hasAll(DexAccessFlags flags) {
548       return false;
549     }
550 
551     @Override
hasNone(DexAccessFlags flags)552     public boolean hasNone(DexAccessFlags flags) {
553       return true;
554     }
555 
556     @Override
isStatic()557     public boolean isStatic() {
558       return false;
559     }
560 
561     @Override
isFinal()562     public boolean isFinal() {
563       return false;
564     }
565 
566     @Override
isAbstract()567     public boolean isAbstract() {
568       return false;
569     }
570 
571     @Override
isBridge()572     public boolean isBridge() {
573       return false;
574     }
575 
576     @Override
getMethod()577     public DexEncodedMethod getMethod() {
578       return null;
579     }
580 
581     @Override
getOriginalSignature()582     public Signature getOriginalSignature() {
583       return null;
584     }
585 
586     @Override
getFinalSignature()587     public Signature getFinalSignature() {
588       return null;
589     }
590   }
591 
592   public class FoundMethodSubject extends MethodSubject {
593 
594     private final FoundClassSubject clazz;
595     private final DexEncodedMethod dexMethod;
596 
FoundMethodSubject(DexEncodedMethod encoded, FoundClassSubject clazz)597     public FoundMethodSubject(DexEncodedMethod encoded, FoundClassSubject clazz) {
598       this.clazz = clazz;
599       this.dexMethod = encoded;
600     }
601 
602     @Override
isPresent()603     public boolean isPresent() {
604       return true;
605     }
606 
607     @Override
isRenamed()608     public boolean isRenamed() {
609       return clazz.naming == null || !getFinalSignature().name.equals(getOriginalSignature().name);
610     }
611 
612     @Override
hasAll(DexAccessFlags flags)613     public boolean hasAll(DexAccessFlags flags) {
614       return dexMethod.accessFlags.containsAllOf(flags);
615     }
616 
617     @Override
hasNone(DexAccessFlags flags)618     public boolean hasNone(DexAccessFlags flags) {
619       return dexMethod.accessFlags.containsNoneOf(flags);
620     }
621 
622     @Override
isStatic()623     public boolean isStatic() {
624       return dexMethod.accessFlags.isStatic();
625     }
626 
627     @Override
isFinal()628     public boolean isFinal() {
629       return dexMethod.accessFlags.isFinal();
630     }
631 
632     @Override
isAbstract()633     public boolean isAbstract() {
634       return dexMethod.accessFlags.isAbstract();
635     }
636 
637     @Override
isBridge()638     public boolean isBridge() {
639       return dexMethod.accessFlags.isBridge();
640     }
641 
642     @Override
getMethod()643     public DexEncodedMethod getMethod() {
644       return dexMethod;
645     }
646 
647     @Override
getOriginalSignature()648     public MethodSignature getOriginalSignature() {
649       MethodSignature signature = getFinalSignature();
650       return clazz.naming != null ?
651           (MethodSignature) clazz.naming.lookup(signature).getOriginalSignature() :
652           signature;
653     }
654 
655     @Override
getFinalSignature()656     public MethodSignature getFinalSignature() {
657       return MemberNaming.MethodSignature.fromDexMethod(dexMethod.method);
658     }
659 
660     @Override
iterateInstructions()661     public Iterator<InstructionSubject> iterateInstructions() {
662       return new InstructionIterator(this);
663     }
664 
665     @Override
iterateInstructions( Predicate<InstructionSubject> filter)666     public <T extends InstructionSubject> Iterator<T> iterateInstructions(
667         Predicate<InstructionSubject> filter) {
668       return new FilteredInstructionIterator<>(this, filter);
669     }
670 
671     @Override
toString()672     public String toString() {
673       return dexMethod.toSourceString();
674     }
675   }
676 
677   public abstract class FieldSubject extends MemberSubject {
678 
getField()679     public abstract DexEncodedField getField();
680   }
681 
682   public class AbsentFieldSubject extends FieldSubject {
683 
684     @Override
hasAll(DexAccessFlags flags)685     public boolean hasAll(DexAccessFlags flags) {
686       return false;
687     }
688 
689     @Override
hasNone(DexAccessFlags flags)690     public boolean hasNone(DexAccessFlags flags) {
691       return true;
692     }
693 
694     @Override
isStatic()695     public boolean isStatic() {
696       return false;
697     }
698 
699     @Override
isFinal()700     public boolean isFinal() {
701       return false;
702     }
703 
704     @Override
isPresent()705     public boolean isPresent() {
706       return false;
707     }
708 
709     @Override
getOriginalSignature()710     public Signature getOriginalSignature() {
711       return null;
712     }
713 
714     @Override
getFinalSignature()715     public Signature getFinalSignature() {
716       return null;
717     }
718 
719     @Override
getField()720     public DexEncodedField getField() {
721       return null;
722     }
723   }
724 
725   public class FoundFieldSubject extends FieldSubject {
726 
727     private final FoundClassSubject clazz;
728     private final DexEncodedField dexField;
729 
FoundFieldSubject(DexEncodedField dexField, FoundClassSubject clazz)730     public FoundFieldSubject(DexEncodedField dexField, FoundClassSubject clazz) {
731       this.clazz = clazz;
732       this.dexField = dexField;
733     }
734 
735     @Override
hasAll(DexAccessFlags flags)736     public boolean hasAll(DexAccessFlags flags) {
737       return dexField.accessFlags.containsAllOf(flags);
738     }
739 
740     @Override
hasNone(DexAccessFlags flags)741     public boolean hasNone(DexAccessFlags flags) {
742       return dexField.accessFlags.containsNoneOf(flags);
743     }
744 
745     @Override
isStatic()746     public boolean isStatic() {
747       return dexField.accessFlags.isStatic();
748     }
749 
750     @Override
isFinal()751     public boolean isFinal() {
752       return dexField.accessFlags.isFinal();
753     }
754 
755     @Override
isPresent()756     public boolean isPresent() {
757       return true;
758     }
759 
type()760     public TypeSubject type() {
761       return new TypeSubject(dexField.field.type);
762     }
763 
764     @Override
getOriginalSignature()765     public FieldSignature getOriginalSignature() {
766       FieldSignature signature = getFinalSignature();
767       return clazz.naming != null ?
768           (FieldSignature) clazz.naming.lookup(signature).getOriginalSignature() :
769           signature;
770     }
771 
772     @Override
getFinalSignature()773     public FieldSignature getFinalSignature() {
774       return MemberNaming.FieldSignature.fromDexField(dexField.field);
775     }
776 
777     @Override
getField()778     public DexEncodedField getField() {
779       return dexField;
780     }
781   }
782 
783   public class TypeSubject extends Subject {
784 
785     private final DexType dexType;
786 
TypeSubject(DexType dexType)787     public TypeSubject(DexType dexType) {
788       this.dexType = dexType;
789     }
790 
791     @Override
isPresent()792     public boolean isPresent() {
793       return true;
794     }
795 
is(String type)796     public boolean is(String type) {
797       return dexType.equals(toDexType(type));
798     }
799 
toString()800     public String toString() {
801       return dexType.toSourceString();
802     }
803   }
804 
805   private class InstructionSubjectFactory {
806 
create(Instruction instruction)807     InstructionSubject create(Instruction instruction) {
808       if (isInvoke(instruction)) {
809         return new InvokeInstructionSubject(this, instruction);
810       } else if (isFieldAccess(instruction)) {
811         return new FieldAccessInstructionSubject(this, instruction);
812       } else {
813         return new InstructionSubject(this, instruction);
814       }
815     }
816 
isInvoke(Instruction instruction)817     boolean isInvoke(Instruction instruction) {
818       return isInvokeVirtual(instruction)
819           || isInvokeInterface(instruction)
820           || isInvokeDirect(instruction)
821           || isInvokeSuper(instruction)
822           || isInvokeStatic(instruction);
823     }
824 
isInvokeVirtual(Instruction instruction)825     boolean isInvokeVirtual(Instruction instruction) {
826       return instruction instanceof InvokeVirtual || instruction instanceof InvokeVirtualRange;
827     }
828 
isInvokeInterface(Instruction instruction)829     boolean isInvokeInterface(Instruction instruction) {
830       return instruction instanceof InvokeInterface || instruction instanceof InvokeInterfaceRange;
831     }
832 
isInvokeDirect(Instruction instruction)833     boolean isInvokeDirect(Instruction instruction) {
834       return instruction instanceof InvokeDirect || instruction instanceof InvokeDirectRange;
835     }
836 
isInvokeSuper(Instruction instruction)837     boolean isInvokeSuper(Instruction instruction) {
838       return instruction instanceof InvokeSuper || instruction instanceof InvokeSuperRange;
839     }
840 
isInvokeStatic(Instruction instruction)841     boolean isInvokeStatic(Instruction instruction) {
842       return instruction instanceof InvokeStatic || instruction instanceof InvokeStaticRange;
843     }
844 
isFieldAccess(Instruction instruction)845     boolean isFieldAccess(Instruction instruction) {
846       return isInstanceGet(instruction)
847           || isInstancePut(instruction)
848           || isStaticGet(instruction)
849           || isStaticSet(instruction);
850     }
851 
isInstanceGet(Instruction instruction)852     boolean isInstanceGet(Instruction instruction) {
853       return instruction instanceof Iget
854           || instruction instanceof IgetBoolean
855           || instruction instanceof IgetByte
856           || instruction instanceof IgetShort
857           || instruction instanceof IgetChar
858           || instruction instanceof IgetWide
859           || instruction instanceof IgetObject;
860     }
861 
isInstancePut(Instruction instruction)862     boolean isInstancePut(Instruction instruction) {
863       return instruction instanceof Iput
864           || instruction instanceof IputBoolean
865           || instruction instanceof IputByte
866           || instruction instanceof IputShort
867           || instruction instanceof IputChar
868           || instruction instanceof IputWide
869           || instruction instanceof IputObject;
870     }
871 
isStaticGet(Instruction instruction)872     boolean isStaticGet(Instruction instruction) {
873       return instruction instanceof Sget
874           || instruction instanceof SgetBoolean
875           || instruction instanceof SgetByte
876           || instruction instanceof SgetShort
877           || instruction instanceof SgetChar
878           || instruction instanceof SgetWide
879           || instruction instanceof SgetObject;
880     }
881 
isStaticSet(Instruction instruction)882     boolean isStaticSet(Instruction instruction) {
883       return instruction instanceof Sput
884           || instruction instanceof SputBoolean
885           || instruction instanceof SputByte
886           || instruction instanceof SputShort
887           || instruction instanceof SputChar
888           || instruction instanceof SputWide
889           || instruction instanceof SputObject;
890     }
891   }
892 
893   public class InstructionSubject {
894 
895     protected final InstructionSubjectFactory factory;
896     protected final Instruction instruction;
897 
InstructionSubject(InstructionSubjectFactory factory, Instruction instruction)898     protected InstructionSubject(InstructionSubjectFactory factory, Instruction instruction) {
899       this.factory = factory;
900       this.instruction = instruction;
901     }
902 
isInvoke()903     public boolean isInvoke() {
904       return factory.isInvoke(instruction);
905     }
906 
isFieldAccess()907     public boolean isFieldAccess() {
908       return factory.isFieldAccess(instruction);
909     }
910 
isInvokeVirtual()911     public boolean isInvokeVirtual() {
912       return factory.isInvokeVirtual(instruction);
913     }
914 
isInvokeInterface()915     public boolean isInvokeInterface() {
916       return factory.isInvokeInterface(instruction);
917     }
918 
isInvokeDirect()919     public boolean isInvokeDirect() {
920       return factory.isInvokeDirect(instruction);
921     }
922 
isInvokeSuper()923     public boolean isInvokeSuper() {
924       return factory.isInvokeSuper(instruction);
925     }
926 
isInvokeStatic()927     public boolean isInvokeStatic() {
928       return factory.isInvokeStatic(instruction);
929     }
930 
isFieldAccess(Instruction instruction)931     boolean isFieldAccess(Instruction instruction) {
932       return factory.isFieldAccess(instruction);
933     }
934   }
935 
936   public class InvokeInstructionSubject extends InstructionSubject {
937 
InvokeInstructionSubject(InstructionSubjectFactory factory, Instruction instruction)938     InvokeInstructionSubject(InstructionSubjectFactory factory, Instruction instruction) {
939       super(factory, instruction);
940       assert isInvoke();
941     }
942 
holder()943     public TypeSubject holder() {
944       return new TypeSubject(invokedMethod().getHolder());
945     }
946 
invokedMethod()947     public DexMethod invokedMethod() {
948       if (instruction instanceof InvokeVirtual) {
949         return ((InvokeVirtual) instruction).getMethod();
950       }
951       if (instruction instanceof InvokeVirtualRange) {
952         return ((InvokeVirtualRange) instruction).getMethod();
953       }
954       if (instruction instanceof InvokeInterface) {
955         return ((InvokeInterface) instruction).getMethod();
956       }
957       if (instruction instanceof InvokeInterfaceRange) {
958         return ((InvokeInterfaceRange) instruction).getMethod();
959       }
960       if (instruction instanceof InvokeDirect) {
961         return ((InvokeDirect) instruction).getMethod();
962       }
963       if (instruction instanceof InvokeDirectRange) {
964         return ((InvokeDirectRange) instruction).getMethod();
965       }
966       if (instruction instanceof InvokeSuper) {
967         return ((InvokeSuper) instruction).getMethod();
968       }
969       if (instruction instanceof InvokeSuperRange) {
970         return ((InvokeSuperRange) instruction).getMethod();
971       }
972       if (instruction instanceof InvokeDirect) {
973         return ((InvokeDirect) instruction).getMethod();
974       }
975       if (instruction instanceof InvokeDirectRange) {
976         return ((InvokeDirectRange) instruction).getMethod();
977       }
978       if (instruction instanceof InvokeStatic) {
979         return ((InvokeStatic) instruction).getMethod();
980       }
981       if (instruction instanceof InvokeStaticRange) {
982         return ((InvokeStaticRange) instruction).getMethod();
983       }
984       assert false;
985       return null;
986     }
987   }
988 
989   public class FieldAccessInstructionSubject extends InstructionSubject {
990 
FieldAccessInstructionSubject(InstructionSubjectFactory factory, Instruction instruction)991     FieldAccessInstructionSubject(InstructionSubjectFactory factory, Instruction instruction) {
992       super(factory, instruction);
993       assert isFieldAccess();
994     }
995 
holder()996     public TypeSubject holder() {
997       return new TypeSubject(accessedField().getHolder());
998     }
999 
accessedField()1000     public DexField accessedField() {
1001       if (instruction instanceof Iget) {
1002         return ((Iget) instruction).getField();
1003       }
1004       if (instruction instanceof IgetBoolean) {
1005         return ((IgetBoolean) instruction).getField();
1006       }
1007       if (instruction instanceof IgetByte) {
1008         return ((IgetByte) instruction).getField();
1009       }
1010       if (instruction instanceof IgetShort) {
1011         return ((IgetShort) instruction).getField();
1012       }
1013       if (instruction instanceof IgetChar) {
1014         return ((IgetChar) instruction).getField();
1015       }
1016       if (instruction instanceof IgetWide) {
1017         return ((IgetWide) instruction).getField();
1018       }
1019       if (instruction instanceof IgetObject) {
1020         return ((IgetObject) instruction).getField();
1021       }
1022       if (instruction instanceof Iput) {
1023         return ((Iput) instruction).getField();
1024       }
1025       if (instruction instanceof IputBoolean) {
1026         return ((IputBoolean) instruction).getField();
1027       }
1028       if (instruction instanceof IputByte) {
1029         return ((IputByte) instruction).getField();
1030       }
1031       if (instruction instanceof IputShort) {
1032         return ((IputShort) instruction).getField();
1033       }
1034       if (instruction instanceof IputChar) {
1035         return ((IputChar) instruction).getField();
1036       }
1037       if (instruction instanceof IputWide) {
1038         return ((IputWide) instruction).getField();
1039       }
1040       if (instruction instanceof IputObject) {
1041         return ((IputObject) instruction).getField();
1042       }
1043       if (instruction instanceof Sget) {
1044         return ((Sget) instruction).getField();
1045       }
1046       if (instruction instanceof SgetBoolean) {
1047         return ((SgetBoolean) instruction).getField();
1048       }
1049       if (instruction instanceof SgetByte) {
1050         return ((SgetByte) instruction).getField();
1051       }
1052       if (instruction instanceof SgetShort) {
1053         return ((SgetShort) instruction).getField();
1054       }
1055       if (instruction instanceof SgetChar) {
1056         return ((SgetChar) instruction).getField();
1057       }
1058       if (instruction instanceof SgetWide) {
1059         return ((SgetWide) instruction).getField();
1060       }
1061       if (instruction instanceof SgetObject) {
1062         return ((SgetObject) instruction).getField();
1063       }
1064       if (instruction instanceof Sput) {
1065         return ((Sput) instruction).getField();
1066       }
1067       if (instruction instanceof SputBoolean) {
1068         return ((SputBoolean) instruction).getField();
1069       }
1070       if (instruction instanceof SputByte) {
1071         return ((SputByte) instruction).getField();
1072       }
1073       if (instruction instanceof SputShort) {
1074         return ((SputShort) instruction).getField();
1075       }
1076       if (instruction instanceof SputChar) {
1077         return ((SputChar) instruction).getField();
1078       }
1079       if (instruction instanceof SputWide) {
1080         return ((SputWide) instruction).getField();
1081       }
1082       if (instruction instanceof SputObject) {
1083         return ((SputObject) instruction).getField();
1084       }
1085       assert false;
1086       return null;
1087     }
1088   }
1089 
1090   private class InstructionIterator implements Iterator<InstructionSubject> {
1091 
1092     private final DexCode code;
1093     private int index;
1094 
InstructionIterator(MethodSubject method)1095     InstructionIterator(MethodSubject method) {
1096       assert method.isPresent();
1097       this.code = method.getMethod().getCode().asDexCode();
1098       this.index = 0;
1099     }
1100 
1101     @Override
hasNext()1102     public boolean hasNext() {
1103       return index < code.instructions.length;
1104     }
1105 
1106     @Override
next()1107     public InstructionSubject next() {
1108       if (index == code.instructions.length) {
1109         throw new NoSuchElementException();
1110       }
1111       return factory.create(code.instructions[index++]);
1112     }
1113   }
1114 
1115   private class FilteredInstructionIterator<T extends InstructionSubject> implements Iterator<T> {
1116 
1117     private final InstructionIterator iterator;
1118     private final Predicate<InstructionSubject> predicate;
1119     private InstructionSubject pendingNext = null;
1120 
FilteredInstructionIterator(MethodSubject method, Predicate<InstructionSubject> predicate)1121     FilteredInstructionIterator(MethodSubject method, Predicate<InstructionSubject> predicate) {
1122       this.iterator = new InstructionIterator(method);
1123       this.predicate = predicate;
1124       hasNext();
1125     }
1126 
1127     @Override
hasNext()1128     public boolean hasNext() {
1129       if (pendingNext == null) {
1130         while (iterator.hasNext()) {
1131           pendingNext = iterator.next();
1132           if (predicate.test(pendingNext)) {
1133             break;
1134           }
1135           pendingNext = null;
1136         }
1137       }
1138       return pendingNext != null;
1139     }
1140 
1141     @Override
next()1142     public T next() {
1143       hasNext();
1144       if (pendingNext == null) {
1145         throw new NoSuchElementException();
1146       }
1147       // We cannot tell if the provided predicate will only match instruction subjects of type T.
1148       @SuppressWarnings("unchecked")
1149       T result = (T) pendingNext;
1150       pendingNext = null;
1151       return result;
1152     }
1153   }
1154 }
1155