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