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.io.InputStream;
32 import java.util.ArrayList;
33 import java.util.Enumeration;
34 import java.util.HashSet;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Map.Entry;
38 import java.util.Set;
39 import java.util.TreeMap;
40 import java.util.regex.Pattern;
41 import java.util.zip.ZipEntry;
42 import 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  */
48 public 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      */
AsmAnalyzer(Log log, List<String> osJarPath, AsmGenerator gen, String[] deriveFrom, String[] includeGlobs, Set<String> excludeClasses, String[] includeFileGlobs)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      */
analyze()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      */
parseZip(List<String> jarPathList, Map<String, ClassReader> classes, Map<String, InputStream> filesFound)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      */
classReaderToClassName(ClassReader classReader)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      */
internalToBinaryClassName(String className)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      */
findIncludes(Map<String, ClassReader> zipClasses)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      */
findClass(String className, Map<String, ClassReader> zipClasses, Map<String, ClassReader> inOutFound)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      */
findGlobs(String globPattern, Map<String, ClassReader> zipClasses, Map<String, ClassReader> inOutFound)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 
getPatternFromGlob(String globPattern)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      */
findClassesDerivingFrom(String super_name, Map<String, ClassReader> zipClasses, Map<String, ClassReader> inOutFound)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      */
getVisitor(Map<String, ClassReader> zipClasses, Map<String, ClassReader> inKeep, Map<String, ClassReader> outKeep, Map<String, ClassReader> inDeps, Map<String, ClassReader> outDeps)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      */
findDeps(Map<String, ClassReader> zipClasses, Map<String, ClassReader> inOutKeepClasses)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 
getOuterClassName(String className)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          */
DependencyVisitor(Map<String, ClassReader> zipClasses, Map<String, ClassReader> inKeep, Map<String, ClassReader> outKeep, Map<String,ClassReader> inDeps, Map<String,ClassReader> outDeps)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 
setClassName(String className)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          */
considerName(String className)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          */
considerNames(String[] classNames)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          */
considerSignature(String signature)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          */
considerType(Type t)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          */
considerDesc(String desc)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
visit(int version, int access, String name, String signature, String superName, String[] interfaces)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
visitAnnotation(String desc, boolean visible)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
visitAttribute(Attribute attr)547         public void visitAttribute(Attribute attr) {
548             // pass
549         }
550 
551         // Visits the end of a class
552         @Override
visitEnd()553         public void visitEnd() {
554             // pass
555         }
556 
557         private class MyFieldVisitor extends FieldVisitor {
558 
MyFieldVisitor()559             public MyFieldVisitor() {
560                 super(Main.ASM_VERSION);
561             }
562 
563             @Override
visitAnnotation(String desc, boolean visible)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
visitAttribute(Attribute attr)571             public void visitAttribute(Attribute attr) {
572                 // pass
573             }
574 
575             // Visits the end of a class
576             @Override
visitEnd()577             public void visitEnd() {
578                 // pass
579             }
580         }
581 
582         @Override
visitField(int access, String name, String desc, String signature, Object value)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
visitInnerClass(String name, String outerName, String innerName, int access)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
visitMethod(int access, String name, String desc, String signature, String[] exceptions)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
visitOuterClass(String owner, String name, String desc)614         public void visitOuterClass(String owner, String name, String desc) {
615             // pass
616         }
617 
618         @Override
visitSource(String source, String debug)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 
MyMethodVisitor(String ownerClass)632             public MyMethodVisitor(String ownerClass) {
633                 super(Main.ASM_VERSION);
634                 mOwnerClass = ownerClass;
635             }
636 
637 
638             @Override
visitAnnotationDefault()639             public AnnotationVisitor visitAnnotationDefault() {
640                 return new MyAnnotationVisitor();
641             }
642 
643             @Override
visitCode()644             public void visitCode() {
645                 // pass
646             }
647 
648             // field instruction
649             @Override
visitFieldInsn(int opcode, String owner, String name, String desc)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
visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2)658             public void visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2) {
659                 // pass
660             }
661 
662             @Override
visitIincInsn(int var, int increment)663             public void visitIincInsn(int var, int increment) {
664                 // pass -- an IINC instruction
665             }
666 
667             @Override
visitInsn(int opcode)668             public void visitInsn(int opcode) {
669                 // pass -- a zero operand instruction
670             }
671 
672             @Override
visitIntInsn(int opcode, int operand)673             public void visitIntInsn(int opcode, int operand) {
674                 // pass -- a single int operand instruction
675             }
676 
677             @Override
visitJumpInsn(int opcode, Label label)678             public void visitJumpInsn(int opcode, Label label) {
679                 // pass -- a jump instruction
680             }
681 
682             @Override
visitLabel(Label label)683             public void visitLabel(Label label) {
684                 // pass -- a label target
685             }
686 
687             // instruction to load a constant from the stack
688             @Override
visitLdcInsn(Object cst)689             public void visitLdcInsn(Object cst) {
690                 if (cst instanceof Type) {
691                     considerType((Type) cst);
692                 }
693             }
694 
695             @Override
visitLineNumber(int line, Label start)696             public void visitLineNumber(int line, Label start) {
697                 // pass
698             }
699 
700             @Override
visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index)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
visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels)711             public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
712                 // pass -- a lookup switch instruction
713             }
714 
715             @Override
visitMaxs(int maxStack, int maxLocals)716             public void visitMaxs(int maxStack, int maxLocals) {
717                 // pass
718             }
719 
720             // instruction that invokes a method
721             @Override
visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf)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
visitMultiANewArrayInsn(String desc, int dims)739             public void visitMultiANewArrayInsn(String desc, int dims) {
740 
741                 // desc an array type descriptor.
742                 considerDesc(desc);
743             }
744 
745             @Override
visitParameterAnnotation(int parameter, String desc, boolean visible)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
visitTableSwitchInsn(int min, int max, Label dflt, Label... labels)754             public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
755                 // pass -- table switch instruction
756 
757             }
758 
759             @Override
visitTryCatchBlock(Label start, Label end, Label handler, String type)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
visitTypeInsn(int opcode, String type)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
visitVarInsn(int opcode, int var)775             public void visitVarInsn(int opcode, int var) {
776                 // pass -- local variable instruction
777             }
778         }
779 
780         private class MySignatureVisitor extends SignatureVisitor {
781 
MySignatureVisitor()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
visitClassType(String name)794             public void visitClassType(String name) {
795                 mCurrentSignatureClass = name;
796                 considerName(name);
797             }
798 
799             // Visits an inner class
800             @Override
visitInnerClassType(String name)801             public void visitInnerClassType(String name) {
802                 if (mCurrentSignatureClass != null) {
803                     mCurrentSignatureClass += "$" + name;
804                     considerName(mCurrentSignatureClass);
805                 }
806             }
807 
808             @Override
visitArrayType()809             public SignatureVisitor visitArrayType() {
810                 return new MySignatureVisitor();
811             }
812 
813             @Override
visitBaseType(char descriptor)814             public void visitBaseType(char descriptor) {
815                 // pass -- a primitive type, ignored
816             }
817 
818             @Override
visitClassBound()819             public SignatureVisitor visitClassBound() {
820                 return new MySignatureVisitor();
821             }
822 
823             @Override
visitExceptionType()824             public SignatureVisitor visitExceptionType() {
825                 return new MySignatureVisitor();
826             }
827 
828             @Override
visitFormalTypeParameter(String name)829             public void visitFormalTypeParameter(String name) {
830                 // pass
831             }
832 
833             @Override
visitInterface()834             public SignatureVisitor visitInterface() {
835                 return new MySignatureVisitor();
836             }
837 
838             @Override
visitInterfaceBound()839             public SignatureVisitor visitInterfaceBound() {
840                 return new MySignatureVisitor();
841             }
842 
843             @Override
visitParameterType()844             public SignatureVisitor visitParameterType() {
845                 return new MySignatureVisitor();
846             }
847 
848             @Override
visitReturnType()849             public SignatureVisitor visitReturnType() {
850                 return new MySignatureVisitor();
851             }
852 
853             @Override
visitSuperclass()854             public SignatureVisitor visitSuperclass() {
855                 return new MySignatureVisitor();
856             }
857 
858             @Override
visitTypeArgument(char wildcard)859             public SignatureVisitor visitTypeArgument(char wildcard) {
860                 return new MySignatureVisitor();
861             }
862 
863             @Override
visitTypeVariable(String name)864             public void visitTypeVariable(String name) {
865                 // pass
866             }
867 
868             @Override
visitTypeArgument()869             public void visitTypeArgument() {
870                 // pass
871             }
872         }
873 
874 
875         // ---------------------------------------------------
876         // --- AnnotationVisitor
877         // ---------------------------------------------------
878 
879         private class MyAnnotationVisitor extends AnnotationVisitor {
880 
MyAnnotationVisitor()881             public MyAnnotationVisitor() {
882                 super(Main.ASM_VERSION);
883             }
884 
885             // Visits a primitive value of an annotation
886             @Override
visit(String name, Object value)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
visitAnnotation(String name, String desc)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
visitArray(String name)903             public AnnotationVisitor visitArray(String name) {
904                 return new MyAnnotationVisitor();
905             }
906 
907             @Override
visitEnum(String name, String desc, String value)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