1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tools.layoutlib.create; 18 19 import com.android.tools.layoutlib.annotations.NotNull; 20 import com.android.tools.layoutlib.annotations.Nullable; 21 22 import org.objectweb.asm.AnnotationVisitor; 23 import org.objectweb.asm.Attribute; 24 import org.objectweb.asm.ClassReader; 25 import org.objectweb.asm.ClassVisitor; 26 import org.objectweb.asm.FieldVisitor; 27 import org.objectweb.asm.Label; 28 import org.objectweb.asm.MethodVisitor; 29 import org.objectweb.asm.Type; 30 import org.objectweb.asm.signature.SignatureReader; 31 import org.objectweb.asm.signature.SignatureVisitor; 32 33 import java.io.FileNotFoundException; 34 import java.io.IOException; 35 import java.io.InputStream; 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.Enumeration; 39 import java.util.HashMap; 40 import java.util.HashSet; 41 import java.util.List; 42 import java.util.Map; 43 import java.util.Map.Entry; 44 import java.util.Set; 45 import java.util.TreeMap; 46 import java.util.concurrent.CompletableFuture; 47 import java.util.concurrent.ForkJoinPool; 48 import java.util.concurrent.ForkJoinTask; 49 import java.util.concurrent.Future; 50 import java.util.function.Consumer; 51 import java.util.regex.Pattern; 52 import java.util.zip.ZipEntry; 53 import java.util.zip.ZipFile; 54 55 /** 56 * Analyzes the input JAR using the ASM java bytecode manipulation library 57 * to list the desired classes and their dependencies. 58 */ 59 public class AsmAnalyzer { 60 61 public static class Result { 62 private final Map<String, ClassReader> mFound; 63 private final Map<String, ClassReader> mDeps; 64 private final Map<String, InputStream> mFilesFound; 65 private final Set<String> mReplaceMethodCallClasses; 66 Result(Map<String, ClassReader> found, Map<String, ClassReader> deps, Map<String, InputStream> filesFound, Set<String> replaceMethodCallClasses)67 private Result(Map<String, ClassReader> found, Map<String, ClassReader> deps, 68 Map<String, InputStream> filesFound, Set<String> replaceMethodCallClasses) { 69 mFound = found; 70 mDeps = deps; 71 mFilesFound = filesFound; 72 mReplaceMethodCallClasses = replaceMethodCallClasses; 73 } 74 getFound()75 public Map<String, ClassReader> getFound() { 76 return mFound; 77 } 78 getDeps()79 public Map<String, ClassReader> getDeps() { 80 return mDeps; 81 } 82 getFilesFound()83 public Map<String, InputStream> getFilesFound() { 84 return mFilesFound; 85 } 86 getReplaceMethodCallClasses()87 public Set<String> getReplaceMethodCallClasses() { 88 return mReplaceMethodCallClasses; 89 } 90 } 91 92 // Note: a bunch of stuff has package-level access for unit tests. Consider it private. 93 94 /** Output logger. */ 95 private final Log mLog; 96 /** The input source JAR to parse. */ 97 private final List<String> mOsSourceJar; 98 /** Keep all classes that derive from these one (these included). */ 99 private final String[] mDeriveFrom; 100 /** Glob patterns of classes to keep, e.g. "com.foo.*" */ 101 private final String[] mIncludeGlobs; 102 /** Glob patterns of classes to exclude.*/ 103 private final String[] mExcludedGlobs; 104 /** Glob patterns of files to keep as is. */ 105 private final String[] mIncludeFileGlobs; 106 /** Internal names of classes that contain method calls that need to be rewritten. */ 107 private final Set<String> mReplaceMethodCallClasses = new HashSet<>(); 108 109 /** 110 * Creates a new analyzer. 111 * 112 * @param log The log output. 113 * @param osJarPath The input source JARs to parse. 114 * @param deriveFrom Keep all classes that derive from these one (these included). 115 * @param includeGlobs Glob patterns of classes to keep, e.g. "com.foo.*" 116 * ("*" does not matches dots whilst "**" does, "." and "$" are interpreted as-is) 117 * @param includeFileGlobs Glob patterns of files which are kept as is. This is only for files 118 * not ending in .class. 119 */ AsmAnalyzer(Log log, List<String> osJarPath, String[] deriveFrom, String[] includeGlobs, String[] excludedGlobs, String[] includeFileGlobs)120 public AsmAnalyzer(Log log, List<String> osJarPath, 121 String[] deriveFrom, String[] includeGlobs, String[] excludedGlobs, 122 String[] includeFileGlobs) { 123 mLog = log; 124 mOsSourceJar = osJarPath != null ? osJarPath : new ArrayList<>(); 125 mDeriveFrom = deriveFrom != null ? deriveFrom : new String[0]; 126 mIncludeGlobs = includeGlobs != null ? includeGlobs : new String[0]; 127 mExcludedGlobs = excludedGlobs != null ? excludedGlobs : new String[0]; 128 mIncludeFileGlobs = includeFileGlobs != null ? includeFileGlobs : new String[0]; 129 } 130 131 /** 132 * Starts the analysis using parameters from the constructor and returns the result. 133 */ 134 @NotNull analyze()135 public Result analyze() throws IOException { 136 Map<String, ClassReader> zipClasses = new TreeMap<>(); 137 Map<String, InputStream> filesFound = new TreeMap<>(); 138 139 parseZip(mOsSourceJar, zipClasses, filesFound); 140 mLog.info("Found %d classes in input JAR%s.", zipClasses.size(), 141 mOsSourceJar.size() > 1 ? "s" : ""); 142 143 Pattern[] includePatterns = Arrays.stream(mIncludeGlobs).parallel() 144 .map(AsmAnalyzer::getPatternFromGlob) 145 .toArray(Pattern[]::new); 146 Pattern[] excludePatterns = Arrays.stream(mExcludedGlobs).parallel() 147 .map(AsmAnalyzer::getPatternFromGlob) 148 .toArray(Pattern[]::new); 149 150 151 Map<String, ClassReader> found = new HashMap<>(); 152 findIncludes(mLog, includePatterns, mDeriveFrom, zipClasses, entry -> { 153 if (!matchesAny(entry.getKey(), excludePatterns)) { 154 found.put(entry.getKey(), entry.getValue()); 155 } 156 }); 157 158 Map<String, ClassReader> deps = new HashMap<>(); 159 findDeps(mLog, zipClasses, found, keepEntry -> { 160 if (!matchesAny(keepEntry.getKey(), excludePatterns)) { 161 found.put(keepEntry.getKey(), keepEntry.getValue()); 162 } 163 }, depEntry -> { 164 if (!matchesAny(depEntry.getKey(), excludePatterns)) { 165 deps.put(depEntry.getKey(), depEntry.getValue()); 166 } 167 }); 168 169 mLog.info("Found %1$d classes to keep, %2$d class dependencies.", 170 found.size(), deps.size()); 171 172 return new Result(found, deps, filesFound, mReplaceMethodCallClasses); 173 } 174 175 /** 176 * Parses a JAR file and adds all the classes found to <code>classes</code> 177 * and all other files to <code>filesFound</code>. 178 * 179 * @param classes The map of class name => ASM ClassReader. Class names are 180 * in the form "android.view.View". 181 * @param filesFound The map of file name => InputStream. The file name is 182 * in the form "android/data/dataFile". 183 */ parseZip(List<String> jarPathList, Map<String, ClassReader> classes, Map<String, InputStream> filesFound)184 void parseZip(List<String> jarPathList, Map<String, ClassReader> classes, 185 Map<String, InputStream> filesFound) throws IOException { 186 if (classes == null || filesFound == null) { 187 return; 188 } 189 190 Pattern[] includeFilePatterns = new Pattern[mIncludeFileGlobs.length]; 191 for (int i = 0; i < mIncludeFileGlobs.length; ++i) { 192 includeFilePatterns[i] = getPatternFromGlob(mIncludeFileGlobs[i]); 193 } 194 195 List<ForkJoinTask<?>> futures = new ArrayList<>(); 196 for (String jarPath : jarPathList) { 197 futures.add(ForkJoinPool.commonPool().submit(() -> { 198 try { 199 ZipFile zip = new ZipFile(jarPath); 200 Enumeration<? extends ZipEntry> entries = zip.entries(); 201 ZipEntry entry; 202 while (entries.hasMoreElements()) { 203 entry = entries.nextElement(); 204 if (entry.getName().endsWith(".class")) { 205 ClassReader cr = new ClassReader(zip.getInputStream(entry)); 206 String className = classReaderToClassName(cr); 207 synchronized (classes) { 208 classes.put(className, cr); 209 } 210 } else { 211 for (Pattern includeFilePattern : includeFilePatterns) { 212 if (includeFilePattern.matcher(entry.getName()).matches()) { 213 synchronized (filesFound) { 214 filesFound.put(entry.getName(), zip.getInputStream(entry)); 215 } 216 break; 217 } 218 } 219 } 220 } 221 } catch (IOException e) { 222 e.printStackTrace(); 223 } 224 })); 225 } 226 227 futures.forEach(ForkJoinTask::join); 228 } 229 230 /** 231 * Utility that returns the fully qualified binary class name for a ClassReader. 232 * E.g. it returns something like android.view.View. 233 */ classReaderToClassName(ClassReader classReader)234 static String classReaderToClassName(ClassReader classReader) { 235 if (classReader == null) { 236 return null; 237 } else { 238 return classReader.getClassName().replace('/', '.'); 239 } 240 } 241 242 /** 243 * Utility that returns the fully qualified binary class name from a path-like FQCN. 244 * E.g. it returns android.view.View from android/view/View. 245 */ internalToBinaryClassName(String className)246 private static String internalToBinaryClassName(String className) { 247 if (className == null) { 248 return null; 249 } else { 250 return className.replace('/', '.'); 251 } 252 } 253 matchesAny(@ullable String className, @NotNull Pattern[] patterns)254 private static boolean matchesAny(@Nullable String className, @NotNull Pattern[] patterns) { 255 for (int i = 0; i < patterns.length; i++) { 256 if (patterns[i].matcher(className).matches()) { 257 return true; 258 } 259 } 260 261 int dollarIdx = className.indexOf('$'); 262 if (dollarIdx != -1) { 263 // This is an inner class, if the outer class matches, we also consider this a match 264 return matchesAny(className.substring(0, dollarIdx), patterns); 265 } 266 267 return false; 268 } 269 270 /** 271 * Process the "includes" arrays. 272 * <p/> 273 * This updates the in_out_found map. 274 */ findIncludes(@otNull Log log, @NotNull Pattern[] includePatterns, @NotNull String[] deriveFrom, @NotNull Map<String, ClassReader> zipClasses, @NotNull Consumer<Entry<String, ClassReader>> newInclude)275 private static void findIncludes(@NotNull Log log, @NotNull Pattern[] includePatterns, 276 @NotNull String[] deriveFrom, @NotNull Map<String, ClassReader> zipClasses, 277 @NotNull Consumer<Entry<String, ClassReader>> newInclude) throws FileNotFoundException { 278 TreeMap<String, ClassReader> found = new TreeMap<>(); 279 280 log.debug("Find classes to include."); 281 282 zipClasses.entrySet().stream() 283 .filter(entry -> matchesAny(entry.getKey(), includePatterns)) 284 .forEach(entry -> found.put(entry.getKey(), entry.getValue())); 285 286 for (String entry : deriveFrom) { 287 findClassesDerivingFrom(entry, zipClasses, found); 288 } 289 290 found.entrySet().forEach(newInclude); 291 } 292 293 294 /** 295 * Uses ASM to find the class reader for the given FQCN class name. 296 * If found, insert it in the in_out_found map. 297 * Returns the class reader object. 298 */ findClass(String className, Map<String, ClassReader> zipClasses, Map<String, ClassReader> inOutFound)299 static ClassReader findClass(String className, Map<String, ClassReader> zipClasses, 300 Map<String, ClassReader> inOutFound) throws FileNotFoundException { 301 ClassReader classReader = zipClasses.get(className); 302 if (classReader == null) { 303 throw new FileNotFoundException(String.format("Class %s not found by ASM", className)); 304 } 305 306 inOutFound.put(className, classReader); 307 return classReader; 308 } 309 310 getPatternFromGlob(String globPattern)311 static Pattern getPatternFromGlob(String globPattern) { 312 // transforms the glob pattern in a regexp: 313 // - escape "." with "\." 314 // - replace "*" by "[^.]*" 315 // - escape "$" with "\$" 316 // - add end-of-line match $ 317 globPattern = globPattern.replaceAll("\\$", "\\\\\\$"); 318 globPattern = globPattern.replaceAll("\\.", "\\\\."); 319 // prevent ** from being altered by the next rule, then process the * rule and finally 320 // the real ** rule (which is now @) 321 globPattern = globPattern.replaceAll("\\*\\*", "@"); 322 globPattern = globPattern.replaceAll("\\*", "[^.]*"); 323 globPattern = globPattern.replaceAll("@", ".*"); 324 globPattern += "$"; 325 326 return Pattern.compile(globPattern); 327 } 328 329 /** 330 * Checks all the classes defined in the JarClassName instance and uses BCEL to 331 * determine if they are derived from the given FQCN super class name. 332 * Inserts the super class and all the class objects found in the map. 333 */ findClassesDerivingFrom(String super_name, Map<String, ClassReader> zipClasses, Map<String, ClassReader> inOutFound)334 static void findClassesDerivingFrom(String super_name, Map<String, ClassReader> zipClasses, 335 Map<String, ClassReader> inOutFound) throws FileNotFoundException { 336 findClass(super_name, zipClasses, inOutFound); 337 338 for (Entry<String, ClassReader> entry : zipClasses.entrySet()) { 339 String className = entry.getKey(); 340 if (super_name.equals(className)) { 341 continue; 342 } 343 ClassReader classReader = entry.getValue(); 344 ClassReader parent_cr = classReader; 345 while (parent_cr != null) { 346 String parent_name = internalToBinaryClassName(parent_cr.getSuperName()); 347 if (parent_name == null) { 348 // not found 349 break; 350 } else if (super_name.equals(parent_name)) { 351 inOutFound.put(className, classReader); 352 break; 353 } 354 parent_cr = zipClasses.get(parent_name); 355 } 356 } 357 } 358 359 /** 360 * Instantiates a new DependencyVisitor. Useful for unit tests. 361 */ getVisitor(Map<String, ClassReader> zipClasses, Map<String, ClassReader> inKeep, Map<String, ClassReader> outKeep, Map<String, ClassReader> inDeps, Map<String, ClassReader> outDeps)362 DependencyVisitor getVisitor(Map<String, ClassReader> zipClasses, 363 Map<String, ClassReader> inKeep, 364 Map<String, ClassReader> outKeep, 365 Map<String, ClassReader> inDeps, 366 Map<String, ClassReader> outDeps) { 367 return new DependencyVisitor(zipClasses, inKeep, outKeep, inDeps, outDeps); 368 } 369 370 /** 371 * Finds all dependencies for all classes in keepClasses which are also 372 * listed in zipClasses. Returns a map of all the dependencies found. 373 */ findDeps(Log log, Map<String, ClassReader> zipClasses, Map<String, ClassReader> inOutKeepClasses, Consumer<Entry<String, ClassReader>> newKeep, Consumer<Entry<String, ClassReader>> newDep)374 void findDeps(Log log, 375 Map<String, ClassReader> zipClasses, 376 Map<String, ClassReader> inOutKeepClasses, 377 Consumer<Entry<String, ClassReader>> newKeep, 378 Consumer<Entry<String, ClassReader>> newDep) { 379 380 TreeMap<String, ClassReader> keep = new TreeMap<>(inOutKeepClasses); 381 TreeMap<String, ClassReader> deps = new TreeMap<>(); 382 TreeMap<String, ClassReader> new_deps = new TreeMap<>(); 383 TreeMap<String, ClassReader> new_keep = new TreeMap<>(); 384 TreeMap<String, ClassReader> temp = new TreeMap<>(); 385 386 DependencyVisitor visitor = getVisitor(zipClasses, 387 keep, new_keep, 388 deps, new_deps); 389 390 for (ClassReader cr : inOutKeepClasses.values()) { 391 visitor.setClassName(cr.getClassName()); 392 cr.accept(visitor, 0 /* flags */); 393 } 394 395 while (new_deps.size() > 0 || new_keep.size() > 0) { 396 new_deps.entrySet().forEach(newDep); 397 new_keep.entrySet().forEach(newKeep); 398 keep.putAll(new_keep); 399 deps.putAll(new_deps); 400 401 temp.clear(); 402 temp.putAll(new_deps); 403 temp.putAll(new_keep); 404 new_deps.clear(); 405 new_keep.clear(); 406 log.debug("Found %1$d to keep, %2$d dependencies.", 407 inOutKeepClasses.size(), deps.size()); 408 409 for (ClassReader cr : temp.values()) { 410 visitor.setClassName(cr.getClassName()); 411 cr.accept(visitor, 0 /* flags */); 412 } 413 } 414 } 415 416 // ---------------------------------- 417 418 /** 419 * Visitor to collect all the type dependencies from a class. 420 */ 421 public class DependencyVisitor extends ClassVisitor { 422 423 /** All classes found in the source JAR. */ 424 private final Map<String, ClassReader> mZipClasses; 425 /** Classes from which dependencies are to be found. */ 426 private final Map<String, ClassReader> mInKeep; 427 /** Dependencies already known. */ 428 private final Map<String, ClassReader> mInDeps; 429 /** New dependencies found by this visitor. */ 430 private final Map<String, ClassReader> mOutDeps; 431 /** New classes to keep as-is found by this visitor. */ 432 private final Map<String, ClassReader> mOutKeep; 433 434 private String mClassName; 435 436 /** 437 * Creates a new visitor that will find all the dependencies for the visited class. 438 * Types which are already in the zipClasses, keepClasses or inDeps are not marked. 439 * New dependencies are marked in outDeps. 440 * 441 * @param zipClasses All classes found in the source JAR. 442 * @param inKeep Classes from which dependencies are to be found. 443 * @param inDeps Dependencies already known. 444 * @param outDeps New dependencies found by this visitor. 445 */ DependencyVisitor(Map<String, ClassReader> zipClasses, Map<String, ClassReader> inKeep, Map<String, ClassReader> outKeep, Map<String,ClassReader> inDeps, Map<String,ClassReader> outDeps)446 public DependencyVisitor(Map<String, ClassReader> zipClasses, 447 Map<String, ClassReader> inKeep, 448 Map<String, ClassReader> outKeep, 449 Map<String,ClassReader> inDeps, 450 Map<String,ClassReader> outDeps) { 451 super(Main.ASM_VERSION); 452 mZipClasses = zipClasses; 453 mInKeep = inKeep; 454 mOutKeep = outKeep; 455 mInDeps = inDeps; 456 mOutDeps = outDeps; 457 } 458 setClassName(String className)459 private void setClassName(String className) { 460 mClassName = className; 461 } 462 463 /** 464 * Considers the given class name as a dependency. 465 * If it does, add to the mOutDeps map. 466 */ considerName(String className)467 public void considerName(String className) { 468 if (className == null) { 469 return; 470 } 471 472 className = internalToBinaryClassName(className); 473 474 // exclude classes that have already been found or are marked to be excluded 475 if (mInKeep.containsKey(className) || 476 mOutKeep.containsKey(className) || 477 mInDeps.containsKey(className) || 478 mOutDeps.containsKey(className)) { 479 return; 480 } 481 482 // exclude classes that are not part of the JAR file being examined 483 ClassReader cr = mZipClasses.get(className); 484 if (cr == null) { 485 return; 486 } 487 488 try { 489 // exclude classes that are part of the default JRE (the one executing this program) 490 if (className.startsWith("java.") || className.startsWith("sun.") || 491 getClass().getClassLoader().loadClass(className) != null) { 492 return; 493 } 494 } catch (ClassNotFoundException e) { 495 // ignore 496 } 497 498 // accept this class: 499 // - android classes are added to dependencies 500 // - non-android classes are added to the list of classes to keep as-is (they don't need 501 // to be stubbed). 502 if (className.contains("android")) { // TODO make configurable 503 mOutDeps.put(className, cr); 504 } else { 505 mOutKeep.put(className, cr); 506 } 507 } 508 509 /** 510 * Considers this array of names using considerName(). 511 */ considerNames(String[] classNames)512 public void considerNames(String[] classNames) { 513 if (classNames != null) { 514 for (String className : classNames) { 515 considerName(className); 516 } 517 } 518 } 519 520 /** 521 * Considers this signature or type signature by invoking the {@link SignatureVisitor} 522 * on it. 523 */ considerSignature(String signature)524 public void considerSignature(String signature) { 525 if (signature != null) { 526 SignatureReader sr = new SignatureReader(signature); 527 // SignatureReader.accept will call accessType so we don't really have 528 // to differentiate where the signature comes from. 529 sr.accept(new MySignatureVisitor()); 530 } 531 } 532 533 /** 534 * Considers this {@link Type}. For arrays, the element type is considered. 535 * If the type is an object, its internal name is considered. If it is a method type, 536 * iterate through the argument and return types. 537 */ considerType(Type t)538 public void considerType(Type t) { 539 if (t != null) { 540 if (t.getSort() == Type.ARRAY) { 541 t = t.getElementType(); 542 } 543 if (t.getSort() == Type.OBJECT) { 544 considerName(t.getInternalName()); 545 } 546 if (t.getSort() == Type.METHOD) { 547 for (Type type : t.getArgumentTypes()) { 548 considerType(type); 549 } 550 considerType(t.getReturnType()); 551 } 552 } 553 } 554 555 /** 556 * Considers a descriptor string. The descriptor is converted to a {@link Type} 557 * and then considerType() is invoked. 558 */ considerDesc(String desc)559 public void considerDesc(String desc) { 560 if (desc != null) { 561 try { 562 Type t = Type.getType(desc); 563 considerType(t); 564 } catch (ArrayIndexOutOfBoundsException e) { 565 // ignore, not a valid type. 566 } 567 } 568 } 569 570 // --------------------------------------------------- 571 // --- ClassVisitor, FieldVisitor 572 // --------------------------------------------------- 573 574 // Visits a class header 575 @Override visit(int version, int access, String name, String signature, String superName, String[] interfaces)576 public void visit(int version, int access, String name, 577 String signature, String superName, String[] interfaces) { 578 // signature is the signature of this class. May be null if the class is not a generic 579 // one, and does not extend or implement generic classes or interfaces. 580 581 if (signature != null) { 582 considerSignature(signature); 583 } 584 585 // superName is the internal of name of the super class (see getInternalName). 586 // For interfaces, the super class is Object. May be null but only for the Object class. 587 considerName(superName); 588 589 // interfaces is the internal names of the class's interfaces (see getInternalName). 590 // May be null. 591 considerNames(interfaces); 592 } 593 594 595 @Override visitAnnotation(String desc, boolean visible)596 public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 597 // desc is the class descriptor of the annotation class. 598 considerDesc(desc); 599 return new MyAnnotationVisitor(); 600 } 601 602 @Override visitAttribute(Attribute attr)603 public void visitAttribute(Attribute attr) { 604 // pass 605 } 606 607 // Visits the end of a class 608 @Override visitEnd()609 public void visitEnd() { 610 // pass 611 } 612 613 private class MyFieldVisitor extends FieldVisitor { 614 MyFieldVisitor()615 public MyFieldVisitor() { 616 super(Main.ASM_VERSION); 617 } 618 619 @Override visitAnnotation(String desc, boolean visible)620 public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 621 // desc is the class descriptor of the annotation class. 622 considerDesc(desc); 623 return new MyAnnotationVisitor(); 624 } 625 626 @Override visitAttribute(Attribute attr)627 public void visitAttribute(Attribute attr) { 628 // pass 629 } 630 631 // Visits the end of a class 632 @Override visitEnd()633 public void visitEnd() { 634 // pass 635 } 636 } 637 638 @Override visitField(int access, String name, String desc, String signature, Object value)639 public FieldVisitor visitField(int access, String name, String desc, 640 String signature, Object value) { 641 // desc is the field's descriptor (see Type). 642 considerDesc(desc); 643 644 // signature is the field's signature. May be null if the field's type does not use 645 // generic types. 646 considerSignature(signature); 647 648 return new MyFieldVisitor(); 649 } 650 651 @Override visitInnerClass(String name, String outerName, String innerName, int access)652 public void visitInnerClass(String name, String outerName, String innerName, int access) { 653 // name is the internal name of an inner class (see getInternalName). 654 considerName(name); 655 } 656 657 @Override visitMethod(int access, String name, String desc, String signature, String[] exceptions)658 public MethodVisitor visitMethod(int access, String name, String desc, 659 String signature, String[] exceptions) { 660 // desc is the method's descriptor (see Type). 661 considerDesc(desc); 662 // signature is the method's signature. May be null if the method parameters, return 663 // type and exceptions do not use generic types. 664 considerSignature(signature); 665 666 return new MyMethodVisitor(mClassName); 667 } 668 669 @Override visitOuterClass(String owner, String name, String desc)670 public void visitOuterClass(String owner, String name, String desc) { 671 // pass 672 } 673 674 @Override visitSource(String source, String debug)675 public void visitSource(String source, String debug) { 676 // pass 677 } 678 679 680 // --------------------------------------------------- 681 // --- MethodVisitor 682 // --------------------------------------------------- 683 684 private class MyMethodVisitor extends MethodVisitor { 685 686 private String mOwnerClass; 687 MyMethodVisitor(String ownerClass)688 public MyMethodVisitor(String ownerClass) { 689 super(Main.ASM_VERSION); 690 mOwnerClass = ownerClass; 691 } 692 693 694 @Override visitAnnotationDefault()695 public AnnotationVisitor visitAnnotationDefault() { 696 return new MyAnnotationVisitor(); 697 } 698 699 @Override visitCode()700 public void visitCode() { 701 // pass 702 } 703 704 // field instruction 705 @Override visitFieldInsn(int opcode, String owner, String name, String desc)706 public void visitFieldInsn(int opcode, String owner, String name, String desc) { 707 // owner is the class that declares the field. 708 considerName(owner); 709 // desc is the field's descriptor (see Type). 710 considerDesc(desc); 711 } 712 713 @Override visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2)714 public void visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2) { 715 // pass 716 } 717 718 @Override visitIincInsn(int var, int increment)719 public void visitIincInsn(int var, int increment) { 720 // pass -- an IINC instruction 721 } 722 723 @Override visitInsn(int opcode)724 public void visitInsn(int opcode) { 725 // pass -- a zero operand instruction 726 } 727 728 @Override visitIntInsn(int opcode, int operand)729 public void visitIntInsn(int opcode, int operand) { 730 // pass -- a single int operand instruction 731 } 732 733 @Override visitJumpInsn(int opcode, Label label)734 public void visitJumpInsn(int opcode, Label label) { 735 // pass -- a jump instruction 736 } 737 738 @Override visitLabel(Label label)739 public void visitLabel(Label label) { 740 // pass -- a label target 741 } 742 743 // instruction to load a constant from the stack 744 @Override visitLdcInsn(Object cst)745 public void visitLdcInsn(Object cst) { 746 if (cst instanceof Type) { 747 considerType((Type) cst); 748 } 749 } 750 751 @Override visitLineNumber(int line, Label start)752 public void visitLineNumber(int line, Label start) { 753 // pass 754 } 755 756 @Override visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index)757 public void visitLocalVariable(String name, String desc, 758 String signature, Label start, Label end, int index) { 759 // desc is the type descriptor of this local variable. 760 considerDesc(desc); 761 // signature is the type signature of this local variable. May be null if the local 762 // variable type does not use generic types. 763 considerSignature(signature); 764 } 765 766 @Override visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels)767 public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { 768 // pass -- a lookup switch instruction 769 } 770 771 @Override visitMaxs(int maxStack, int maxLocals)772 public void visitMaxs(int maxStack, int maxLocals) { 773 // pass 774 } 775 776 // instruction that invokes a method 777 @Override visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf)778 public void visitMethodInsn(int opcode, String owner, String name, String desc, 779 boolean itf) { 780 781 // owner is the internal name of the method's owner class 782 considerName(owner); 783 // desc is the method's descriptor (see Type). 784 considerDesc(desc); 785 786 787 // Check if method needs to replaced by a call to a different method. 788 if (ReplaceMethodCallsAdapter.isReplacementNeeded(owner, name, desc, mOwnerClass)) { 789 mReplaceMethodCallClasses.add(mOwnerClass); 790 } 791 } 792 793 // instruction multianewarray, whatever that is 794 @Override visitMultiANewArrayInsn(String desc, int dims)795 public void visitMultiANewArrayInsn(String desc, int dims) { 796 797 // desc an array type descriptor. 798 considerDesc(desc); 799 } 800 801 @Override visitParameterAnnotation(int parameter, String desc, boolean visible)802 public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, 803 boolean visible) { 804 // desc is the class descriptor of the annotation class. 805 considerDesc(desc); 806 return new MyAnnotationVisitor(); 807 } 808 809 @Override visitTableSwitchInsn(int min, int max, Label dflt, Label... labels)810 public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { 811 // pass -- table switch instruction 812 813 } 814 815 @Override visitTryCatchBlock(Label start, Label end, Label handler, String type)816 public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { 817 // type is the internal name of the type of exceptions handled by the handler, 818 // or null to catch any exceptions (for "finally" blocks). 819 considerName(type); 820 } 821 822 // type instruction 823 @Override visitTypeInsn(int opcode, String type)824 public void visitTypeInsn(int opcode, String type) { 825 // type is the operand of the instruction to be visited. This operand must be the 826 // internal name of an object or array class. 827 considerName(type); 828 } 829 830 @Override visitVarInsn(int opcode, int var)831 public void visitVarInsn(int opcode, int var) { 832 // pass -- local variable instruction 833 } 834 } 835 836 private class MySignatureVisitor extends SignatureVisitor { 837 MySignatureVisitor()838 public MySignatureVisitor() { 839 super(Main.ASM_VERSION); 840 } 841 842 // --------------------------------------------------- 843 // --- SignatureVisitor 844 // --------------------------------------------------- 845 846 private String mCurrentSignatureClass = null; 847 848 // Starts the visit of a signature corresponding to a class or interface type 849 @Override visitClassType(String name)850 public void visitClassType(String name) { 851 mCurrentSignatureClass = name; 852 considerName(name); 853 } 854 855 // Visits an inner class 856 @Override visitInnerClassType(String name)857 public void visitInnerClassType(String name) { 858 if (mCurrentSignatureClass != null) { 859 mCurrentSignatureClass += "$" + name; 860 considerName(mCurrentSignatureClass); 861 } 862 } 863 864 @Override visitArrayType()865 public SignatureVisitor visitArrayType() { 866 return new MySignatureVisitor(); 867 } 868 869 @Override visitBaseType(char descriptor)870 public void visitBaseType(char descriptor) { 871 // pass -- a primitive type, ignored 872 } 873 874 @Override visitClassBound()875 public SignatureVisitor visitClassBound() { 876 return new MySignatureVisitor(); 877 } 878 879 @Override visitExceptionType()880 public SignatureVisitor visitExceptionType() { 881 return new MySignatureVisitor(); 882 } 883 884 @Override visitFormalTypeParameter(String name)885 public void visitFormalTypeParameter(String name) { 886 // pass 887 } 888 889 @Override visitInterface()890 public SignatureVisitor visitInterface() { 891 return new MySignatureVisitor(); 892 } 893 894 @Override visitInterfaceBound()895 public SignatureVisitor visitInterfaceBound() { 896 return new MySignatureVisitor(); 897 } 898 899 @Override visitParameterType()900 public SignatureVisitor visitParameterType() { 901 return new MySignatureVisitor(); 902 } 903 904 @Override visitReturnType()905 public SignatureVisitor visitReturnType() { 906 return new MySignatureVisitor(); 907 } 908 909 @Override visitSuperclass()910 public SignatureVisitor visitSuperclass() { 911 return new MySignatureVisitor(); 912 } 913 914 @Override visitTypeArgument(char wildcard)915 public SignatureVisitor visitTypeArgument(char wildcard) { 916 return new MySignatureVisitor(); 917 } 918 919 @Override visitTypeVariable(String name)920 public void visitTypeVariable(String name) { 921 // pass 922 } 923 924 @Override visitTypeArgument()925 public void visitTypeArgument() { 926 // pass 927 } 928 } 929 930 931 // --------------------------------------------------- 932 // --- AnnotationVisitor 933 // --------------------------------------------------- 934 935 private class MyAnnotationVisitor extends AnnotationVisitor { 936 MyAnnotationVisitor()937 public MyAnnotationVisitor() { 938 super(Main.ASM_VERSION); 939 } 940 941 // Visits a primitive value of an annotation 942 @Override visit(String name, Object value)943 public void visit(String name, Object value) { 944 // value is the actual value, whose type must be Byte, Boolean, Character, Short, 945 // Integer, Long, Float, Double, String or Type 946 if (value instanceof Type) { 947 considerType((Type) value); 948 } 949 } 950 951 @Override visitAnnotation(String name, String desc)952 public AnnotationVisitor visitAnnotation(String name, String desc) { 953 // desc is the class descriptor of the nested annotation class. 954 considerDesc(desc); 955 return new MyAnnotationVisitor(); 956 } 957 958 @Override visitArray(String name)959 public AnnotationVisitor visitArray(String name) { 960 return new MyAnnotationVisitor(); 961 } 962 963 @Override visitEnum(String name, String desc, String value)964 public void visitEnum(String name, String desc, String value) { 965 // desc is the class descriptor of the enumeration class. 966 considerDesc(desc); 967 } 968 } 969 } 970 } 971