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