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