1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.tools.layoutlib.create;
18 
19 import com.android.tools.layoutlib.annotations.VisibleForTesting;
20 import com.android.tools.layoutlib.annotations.VisibleForTesting.Visibility;
21 
22 import org.objectweb.asm.AnnotationVisitor;
23 import org.objectweb.asm.Attribute;
24 import org.objectweb.asm.ClassReader;
25 import org.objectweb.asm.ClassVisitor;
26 import org.objectweb.asm.FieldVisitor;
27 import org.objectweb.asm.Label;
28 import org.objectweb.asm.MethodVisitor;
29 import org.objectweb.asm.Type;
30 import org.objectweb.asm.signature.SignatureReader;
31 import org.objectweb.asm.signature.SignatureVisitor;
32 
33 import java.io.IOException;
34 import java.util.ArrayList;
35 import java.util.Enumeration;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Map.Entry;
39 import java.util.Set;
40 import java.util.TreeMap;
41 import java.util.TreeSet;
42 import java.util.zip.ZipEntry;
43 import java.util.zip.ZipFile;
44 
45 /**
46  * Analyzes the input JAR using the ASM java bytecode manipulation library
47  * to list the classes and their dependencies. A "dependency" is a class
48  * used by another class.
49  */
50 public class DependencyFinder {
51 
52     // Note: a bunch of stuff has package-level access for unit tests. Consider it private.
53 
54     /** Output logger. */
55     private final Log mLog;
56 
57     /**
58      * Creates a new analyzer.
59      *
60      * @param log The log output.
61      */
DependencyFinder(Log log)62     public DependencyFinder(Log log) {
63         mLog = log;
64     }
65 
66     /**
67      * Starts the analysis using parameters from the constructor.
68      *
69      * @param osJarPath The input source JARs to parse.
70      * @return A pair: [0]: map { class FQCN => set of FQCN class dependencies }.
71      *                 [1]: map { missing class FQCN => set of FQCN class that uses it. }
72      */
findDeps(List<String> osJarPath)73     public List<Map<String, Set<String>>> findDeps(List<String> osJarPath) throws IOException {
74 
75         Map<String, ClassReader> zipClasses = parseZip(osJarPath);
76         mLog.info("Found %d classes in input JAR%s.",
77                 zipClasses.size(),
78                 osJarPath.size() > 1 ? "s" : "");
79 
80         Map<String, Set<String>> deps = findClassesDeps(zipClasses);
81 
82         Map<String, Set<String>> missing = findMissingClasses(deps, zipClasses.keySet());
83 
84         List<Map<String, Set<String>>> result = new ArrayList<>(2);
85         result.add(deps);
86         result.add(missing);
87         return result;
88     }
89 
90     /**
91      * Prints dependencies to the current logger, found stuff and missing stuff.
92      */
printAllDeps(List<Map<String, Set<String>>> result)93     public void printAllDeps(List<Map<String, Set<String>>> result) {
94         assert result.size() == 2;
95         Map<String, Set<String>> deps = result.get(0);
96         Map<String, Set<String>> missing = result.get(1);
97 
98         // Print all dependences found in the format:
99         // +Found: <FQCN from zip>
100         //     uses: FQCN
101 
102         mLog.info("++++++ %d Entries found in source JARs", deps.size());
103         mLog.info("");
104 
105         for (Entry<String, Set<String>> entry : deps.entrySet()) {
106             mLog.info(    "+Found  : %s", entry.getKey());
107             for (String dep : entry.getValue()) {
108                 mLog.info("    uses: %s", dep);
109             }
110 
111             mLog.info("");
112         }
113 
114 
115         // Now print all missing dependences in the format:
116         // -Missing <FQCN>:
117         //     used by: <FQCN>
118 
119         mLog.info("");
120         mLog.info("------ %d Entries missing from source JARs", missing.size());
121         mLog.info("");
122 
123         for (Entry<String, Set<String>> entry : missing.entrySet()) {
124             mLog.info(    "-Missing  : %s", entry.getKey());
125             for (String dep : entry.getValue()) {
126                 mLog.info("   used by: %s", dep);
127             }
128 
129             mLog.info("");
130         }
131     }
132 
133     /**
134      * Prints only a summary of the missing dependencies to the current logger.
135      */
printMissingDeps(List<Map<String, Set<String>>> result)136     public void printMissingDeps(List<Map<String, Set<String>>> result) {
137         assert result.size() == 2;
138         @SuppressWarnings("unused") Map<String, Set<String>> deps = result.get(0);
139         Map<String, Set<String>> missing = result.get(1);
140 
141         for (String fqcn : missing.keySet()) {
142             mLog.info("%s", fqcn);
143         }
144     }
145 
146     // ----------------
147 
148     /**
149      * Parses a JAR file and returns a list of all classes founds using a map
150      * class name => ASM ClassReader. Class names are in the form "android.view.View".
151      */
parseZip(List<String> jarPathList)152     Map<String,ClassReader> parseZip(List<String> jarPathList) throws IOException {
153         TreeMap<String, ClassReader> classes = new TreeMap<>();
154 
155         for (String jarPath : jarPathList) {
156             ZipFile zip = new ZipFile(jarPath);
157             Enumeration<? extends ZipEntry> entries = zip.entries();
158             ZipEntry entry;
159             while (entries.hasMoreElements()) {
160                 entry = entries.nextElement();
161                 if (entry.getName().endsWith(".class")) {
162                     ClassReader cr = new ClassReader(zip.getInputStream(entry));
163                     String className = classReaderToClassName(cr);
164                     classes.put(className, cr);
165                 }
166             }
167         }
168 
169         return classes;
170     }
171 
172     /**
173      * Utility that returns the fully qualified binary class name for a ClassReader.
174      * E.g. it returns something like android.view.View.
175      */
classReaderToClassName(ClassReader classReader)176     static String classReaderToClassName(ClassReader classReader) {
177         if (classReader == null) {
178             return null;
179         } else {
180             return classReader.getClassName().replace('/', '.');
181         }
182     }
183 
184     /**
185      * Utility that returns the fully qualified binary class name from a path-like FQCN.
186      * E.g. it returns android.view.View from android/view/View.
187      */
internalToBinaryClassName(String className)188     static String internalToBinaryClassName(String className) {
189         if (className == null) {
190             return null;
191         } else {
192             return className.replace('/', '.');
193         }
194     }
195 
196     /**
197      * Finds all dependencies for all classes in keepClasses which are also
198      * listed in zipClasses. Returns a map of all the dependencies found.
199      */
findClassesDeps(Map<String, ClassReader> zipClasses)200     Map<String, Set<String>> findClassesDeps(Map<String, ClassReader> zipClasses) {
201 
202         // The dependencies that we'll collect.
203         // It's a map Class name => uses class names.
204         Map<String, Set<String>> dependencyMap = new TreeMap<>();
205 
206         DependencyVisitor visitor = getVisitor();
207 
208         int count = 0;
209         try {
210             for (Entry<String, ClassReader> entry : zipClasses.entrySet()) {
211                 String name = entry.getKey();
212 
213                 TreeSet<String> set = new TreeSet<>();
214                 dependencyMap.put(name, set);
215                 visitor.setDependencySet(set);
216 
217                 ClassReader cr = entry.getValue();
218                 cr.accept(visitor, 0 /* flags */);
219 
220                 visitor.setDependencySet(null);
221 
222                 mLog.debugNoln("Visited %d classes\r", ++count);
223             }
224         } finally {
225             mLog.debugNoln("\n");
226         }
227 
228         return dependencyMap;
229     }
230 
231     /**
232      * Computes which classes FQCN were found as dependencies that are NOT listed
233      * in the original JAR classes.
234      *
235      * @param deps The map { FQCN => dependencies[] } returned by {@link #findClassesDeps(Map)}.
236      * @param zipClasses The set of all classes FQCN found in the JAR files.
237      * @return A map { FQCN not found in the zipClasses => classes using it }
238      */
findMissingClasses( Map<String, Set<String>> deps, Set<String> zipClasses)239     private Map<String, Set<String>> findMissingClasses(
240             Map<String, Set<String>> deps,
241             Set<String> zipClasses) {
242         Map<String, Set<String>> missing = new TreeMap<>();
243 
244         for (Entry<String, Set<String>> entry : deps.entrySet()) {
245             String name = entry.getKey();
246 
247             for (String dep : entry.getValue()) {
248                 if (!zipClasses.contains(dep)) {
249                     // This dependency doesn't exist in the zip classes.
250                     Set<String> set = missing.get(dep);
251                     if (set == null) {
252                         set = new TreeSet<>();
253                         missing.put(dep, set);
254                     }
255                     set.add(name);
256                 }
257             }
258 
259         }
260 
261         return missing;
262     }
263 
264 
265     // ----------------------------------
266 
267     /**
268      * Instantiates a new DependencyVisitor. Useful for unit tests.
269      */
270     @VisibleForTesting(visibility=Visibility.PRIVATE)
getVisitor()271     DependencyVisitor getVisitor() {
272         return new DependencyVisitor();
273     }
274 
275     /**
276      * Visitor to collect all the type dependencies from a class.
277      */
278     public class DependencyVisitor extends ClassVisitor {
279 
280         private Set<String> mCurrentDepSet;
281 
282         /**
283          * Creates a new visitor that will find all the dependencies for the visited class.
284          */
DependencyVisitor()285         public DependencyVisitor() {
286             super(Main.ASM_VERSION);
287         }
288 
289         /**
290          * Sets the {@link Set} where to record direct dependencies for this class.
291          * This will change before each {@link ClassReader#accept(ClassVisitor, int)} call.
292          */
setDependencySet(Set<String> set)293         public void setDependencySet(Set<String> set) {
294             mCurrentDepSet = set;
295         }
296 
297         /**
298          * Considers the given class name as a dependency.
299          */
considerName(String className)300         public void considerName(String className) {
301             if (className == null) {
302                 return;
303             }
304 
305             className = internalToBinaryClassName(className);
306 
307             try {
308                 // exclude classes that are part of the default JRE (the one executing this program)
309                 // or in java package (we won't be able to load them anyway).
310                 if (className.startsWith("java.") ||
311                         getClass().getClassLoader().loadClass(className) != null) {
312                     return;
313                 }
314             } catch (ClassNotFoundException e) {
315                 // ignore
316             }
317 
318             // Add it to the dependency set for the currently visited class, as needed.
319             assert mCurrentDepSet != null;
320             mCurrentDepSet.add(className);
321         }
322 
323         /**
324          * Considers this array of names using considerName().
325          */
considerNames(String[] classNames)326         public void considerNames(String[] classNames) {
327             if (classNames != null) {
328                 for (String className : classNames) {
329                     considerName(className);
330                 }
331             }
332         }
333 
334         /**
335          * Considers this signature or type signature by invoking the {@link SignatureVisitor}
336          * on it.
337          */
considerSignature(String signature)338         public void considerSignature(String signature) {
339             if (signature != null) {
340                 SignatureReader sr = new SignatureReader(signature);
341                 // SignatureReader.accept will call accessType so we don't really have
342                 // to differentiate where the signature comes from.
343                 sr.accept(new MySignatureVisitor());
344             }
345         }
346 
347         /**
348          * Considers this {@link Type}. For arrays, the element type is considered.
349          * If the type is an object, it's internal name is considered.
350          */
considerType(Type t)351         public void considerType(Type t) {
352             if (t != null) {
353                 if (t.getSort() == Type.ARRAY) {
354                     t = t.getElementType();
355                 }
356                 if (t.getSort() == Type.OBJECT) {
357                     considerName(t.getInternalName());
358                 }
359             }
360         }
361 
362         /**
363          * Considers a descriptor string. The descriptor is converted to a {@link Type}
364          * and then considerType() is invoked.
365          */
considerDesc(String desc)366         public boolean considerDesc(String desc) {
367             if (desc != null) {
368                 try {
369                     if (desc.length() > 0 && desc.charAt(0) == '(') {
370                         // This is a method descriptor with arguments and a return type.
371                         Type t = Type.getReturnType(desc);
372                         considerType(t);
373 
374                         for (Type arg : Type.getArgumentTypes(desc)) {
375                             considerType(arg);
376                         }
377 
378                     } else {
379                         Type t = Type.getType(desc);
380                         considerType(t);
381                     }
382                     return true;
383                 } catch (ArrayIndexOutOfBoundsException e) {
384                     // ignore, not a valid type.
385                 }
386             }
387             return false;
388         }
389 
390 
391         // ---------------------------------------------------
392         // --- ClassVisitor, FieldVisitor
393         // ---------------------------------------------------
394 
395         // Visits a class header
396         @Override
visit(int version, int access, String name, String signature, String superName, String[] interfaces)397         public void visit(int version, int access, String name,
398                 String signature, String superName, String[] interfaces) {
399             // signature is the signature of this class. May be null if the class is not a generic
400             // one, and does not extend or implement generic classes or interfaces.
401 
402             if (signature != null) {
403                 considerSignature(signature);
404             }
405 
406             // superName is the internal of name of the super class (see getInternalName).
407             // For interfaces, the super class is Object. May be null but only for the Object class.
408             considerName(superName);
409 
410             // interfaces is the internal names of the class's interfaces (see getInternalName).
411             // May be null.
412             considerNames(interfaces);
413         }
414 
415 
416         @Override
visitAnnotation(String desc, boolean visible)417         public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
418             // desc is the class descriptor of the annotation class.
419             considerDesc(desc);
420             return new MyAnnotationVisitor();
421         }
422 
423         @Override
visitAttribute(Attribute attr)424         public void visitAttribute(Attribute attr) {
425             // pass
426         }
427 
428         // Visits the end of a class
429         @Override
visitEnd()430         public void visitEnd() {
431             // pass
432         }
433 
434         private class MyFieldVisitor extends FieldVisitor {
435 
MyFieldVisitor()436             public MyFieldVisitor() {
437                 super(Main.ASM_VERSION);
438             }
439 
440             @Override
visitAnnotation(String desc, boolean visible)441             public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
442                 // desc is the class descriptor of the annotation class.
443                 considerDesc(desc);
444                 return new MyAnnotationVisitor();
445             }
446 
447             @Override
visitAttribute(Attribute attr)448             public void visitAttribute(Attribute attr) {
449                 // pass
450             }
451 
452             // Visits the end of a class
453             @Override
visitEnd()454             public void visitEnd() {
455                 // pass
456             }
457         }
458 
459         @Override
visitField(int access, String name, String desc, String signature, Object value)460         public FieldVisitor visitField(int access, String name, String desc,
461                 String signature, Object value) {
462             // desc is the field's descriptor (see Type).
463             considerDesc(desc);
464 
465             // signature is the field's signature. May be null if the field's type does not use
466             // generic types.
467             considerSignature(signature);
468 
469             return new MyFieldVisitor();
470         }
471 
472         @Override
visitInnerClass(String name, String outerName, String innerName, int access)473         public void visitInnerClass(String name, String outerName, String innerName, int access) {
474             // name is the internal name of an inner class (see getInternalName).
475             // Note: outerName/innerName seems to be null when we're reading the
476             // _Original_ClassName classes generated by layoutlib_create.
477             if (outerName != null) {
478                 considerName(name);
479             }
480         }
481 
482         @Override
visitMethod(int access, String name, String desc, String signature, String[] exceptions)483         public MethodVisitor visitMethod(int access, String name, String desc,
484                 String signature, String[] exceptions) {
485             // desc is the method's descriptor (see Type).
486             considerDesc(desc);
487             // signature is the method's signature. May be null if the method parameters, return
488             // type and exceptions do not use generic types.
489             considerSignature(signature);
490 
491             return new MyMethodVisitor();
492         }
493 
494         @Override
visitOuterClass(String owner, String name, String desc)495         public void visitOuterClass(String owner, String name, String desc) {
496             // pass
497         }
498 
499         @Override
visitSource(String source, String debug)500         public void visitSource(String source, String debug) {
501             // pass
502         }
503 
504 
505         // ---------------------------------------------------
506         // --- MethodVisitor
507         // ---------------------------------------------------
508 
509         private class MyMethodVisitor extends MethodVisitor {
510 
MyMethodVisitor()511             public MyMethodVisitor() {
512                 super(Main.ASM_VERSION);
513             }
514 
515 
516             @Override
visitAnnotationDefault()517             public AnnotationVisitor visitAnnotationDefault() {
518                 return new MyAnnotationVisitor();
519             }
520 
521             @Override
visitCode()522             public void visitCode() {
523                 // pass
524             }
525 
526             // field instruction
527             @Override
visitFieldInsn(int opcode, String owner, String name, String desc)528             public void visitFieldInsn(int opcode, String owner, String name, String desc) {
529                 // owner is the class that declares the field.
530                 considerName(owner);
531                 // desc is the field's descriptor (see Type).
532                 considerDesc(desc);
533             }
534 
535             @Override
visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2)536             public void visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2) {
537                 // pass
538             }
539 
540             @Override
visitIincInsn(int var, int increment)541             public void visitIincInsn(int var, int increment) {
542                 // pass -- an IINC instruction
543             }
544 
545             @Override
visitInsn(int opcode)546             public void visitInsn(int opcode) {
547                 // pass -- a zero operand instruction
548             }
549 
550             @Override
visitIntInsn(int opcode, int operand)551             public void visitIntInsn(int opcode, int operand) {
552                 // pass -- a single int operand instruction
553             }
554 
555             @Override
visitJumpInsn(int opcode, Label label)556             public void visitJumpInsn(int opcode, Label label) {
557                 // pass -- a jump instruction
558             }
559 
560             @Override
visitLabel(Label label)561             public void visitLabel(Label label) {
562                 // pass -- a label target
563             }
564 
565             // instruction to load a constant from the stack
566             @Override
visitLdcInsn(Object cst)567             public void visitLdcInsn(Object cst) {
568                 if (cst instanceof Type) {
569                     considerType((Type) cst);
570                 }
571             }
572 
573             @Override
visitLineNumber(int line, Label start)574             public void visitLineNumber(int line, Label start) {
575                 // pass
576             }
577 
578             @Override
visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index)579             public void visitLocalVariable(String name, String desc,
580                     String signature, Label start, Label end, int index) {
581                 // desc is the type descriptor of this local variable.
582                 considerDesc(desc);
583                 // signature is the type signature of this local variable. May be null if the local
584                 // variable type does not use generic types.
585                 considerSignature(signature);
586             }
587 
588             @Override
visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels)589             public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
590                 // pass -- a lookup switch instruction
591             }
592 
593             @Override
visitMaxs(int maxStack, int maxLocals)594             public void visitMaxs(int maxStack, int maxLocals) {
595                 // pass
596             }
597 
598             // instruction that invokes a method
599             @Override
visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf)600             public void visitMethodInsn(int opcode, String owner, String name, String desc,
601                     boolean itf) {
602 
603                 // owner is the internal name of the method's owner class
604                 if (!considerDesc(owner) && owner.indexOf('/') != -1) {
605                     considerName(owner);
606                 }
607                 // desc is the method's descriptor (see Type).
608                 considerDesc(desc);
609             }
610 
611             // instruction multianewarray, whatever that is
612             @Override
visitMultiANewArrayInsn(String desc, int dims)613             public void visitMultiANewArrayInsn(String desc, int dims) {
614 
615                 // desc an array type descriptor.
616                 considerDesc(desc);
617             }
618 
619             @Override
visitParameterAnnotation(int parameter, String desc, boolean visible)620             public AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
621                     boolean visible) {
622                 // desc is the class descriptor of the annotation class.
623                 considerDesc(desc);
624                 return new MyAnnotationVisitor();
625             }
626 
627             @Override
visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels)628             public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
629                 // pass -- table switch instruction
630 
631             }
632 
633             @Override
visitTryCatchBlock(Label start, Label end, Label handler, String type)634             public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
635                 // type is the internal name of the type of exceptions handled by the handler,
636                 // or null to catch any exceptions (for "finally" blocks).
637                 considerName(type);
638             }
639 
640             // type instruction
641             @Override
visitTypeInsn(int opcode, String type)642             public void visitTypeInsn(int opcode, String type) {
643                 // type is the operand of the instruction to be visited. This operand must be the
644                 // internal name of an object or array class.
645                 considerName(type);
646             }
647 
648             @Override
visitVarInsn(int opcode, int var)649             public void visitVarInsn(int opcode, int var) {
650                 // pass -- local variable instruction
651             }
652         }
653 
654         private class MySignatureVisitor extends SignatureVisitor {
655 
MySignatureVisitor()656             public MySignatureVisitor() {
657                 super(Main.ASM_VERSION);
658             }
659 
660             // ---------------------------------------------------
661             // --- SignatureVisitor
662             // ---------------------------------------------------
663 
664             private String mCurrentSignatureClass = null;
665 
666             // Starts the visit of a signature corresponding to a class or interface type
667             @Override
visitClassType(String name)668             public void visitClassType(String name) {
669                 mCurrentSignatureClass = name;
670                 considerName(name);
671             }
672 
673             // Visits an inner class
674             @Override
visitInnerClassType(String name)675             public void visitInnerClassType(String name) {
676                 if (mCurrentSignatureClass != null) {
677                     mCurrentSignatureClass += "$" + name;
678                     considerName(mCurrentSignatureClass);
679                 }
680             }
681 
682             @Override
visitArrayType()683             public SignatureVisitor visitArrayType() {
684                 return new MySignatureVisitor();
685             }
686 
687             @Override
visitBaseType(char descriptor)688             public void visitBaseType(char descriptor) {
689                 // pass -- a primitive type, ignored
690             }
691 
692             @Override
visitClassBound()693             public SignatureVisitor visitClassBound() {
694                 return new MySignatureVisitor();
695             }
696 
697             @Override
visitExceptionType()698             public SignatureVisitor visitExceptionType() {
699                 return new MySignatureVisitor();
700             }
701 
702             @Override
visitFormalTypeParameter(String name)703             public void visitFormalTypeParameter(String name) {
704                 // pass
705             }
706 
707             @Override
visitInterface()708             public SignatureVisitor visitInterface() {
709                 return new MySignatureVisitor();
710             }
711 
712             @Override
visitInterfaceBound()713             public SignatureVisitor visitInterfaceBound() {
714                 return new MySignatureVisitor();
715             }
716 
717             @Override
visitParameterType()718             public SignatureVisitor visitParameterType() {
719                 return new MySignatureVisitor();
720             }
721 
722             @Override
visitReturnType()723             public SignatureVisitor visitReturnType() {
724                 return new MySignatureVisitor();
725             }
726 
727             @Override
visitSuperclass()728             public SignatureVisitor visitSuperclass() {
729                 return new MySignatureVisitor();
730             }
731 
732             @Override
visitTypeArgument(char wildcard)733             public SignatureVisitor visitTypeArgument(char wildcard) {
734                 return new MySignatureVisitor();
735             }
736 
737             @Override
visitTypeVariable(String name)738             public void visitTypeVariable(String name) {
739                 // pass
740             }
741 
742             @Override
visitTypeArgument()743             public void visitTypeArgument() {
744                 // pass
745             }
746         }
747 
748 
749         // ---------------------------------------------------
750         // --- AnnotationVisitor
751         // ---------------------------------------------------
752 
753         private class MyAnnotationVisitor extends AnnotationVisitor {
754 
MyAnnotationVisitor()755             public MyAnnotationVisitor() {
756                 super(Main.ASM_VERSION);
757             }
758 
759             // Visits a primitive value of an annotation
760             @Override
visit(String name, Object value)761             public void visit(String name, Object value) {
762                 // value is the actual value, whose type must be Byte, Boolean, Character, Short,
763                 // Integer, Long, Float, Double, String or Type
764                 if (value instanceof Type) {
765                     considerType((Type) value);
766                 }
767             }
768 
769             @Override
visitAnnotation(String name, String desc)770             public AnnotationVisitor visitAnnotation(String name, String desc) {
771                 // desc is the class descriptor of the nested annotation class.
772                 considerDesc(desc);
773                 return new MyAnnotationVisitor();
774             }
775 
776             @Override
visitArray(String name)777             public AnnotationVisitor visitArray(String name) {
778                 return new MyAnnotationVisitor();
779             }
780 
781             @Override
visitEnum(String name, String desc, String value)782             public void visitEnum(String name, String desc, String value) {
783                 // desc is the class descriptor of the enumeration class.
784                 considerDesc(desc);
785             }
786         }
787     }
788 }
789