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