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