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