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