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