1 /*
2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
3  *             of Java bytecode.
4  *
5  * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu)
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the Free
9  * Software Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15  * more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  */
21 package proguard.shrink;
22 
23 import proguard.classfile.*;
24 import proguard.classfile.attribute.*;
25 import proguard.classfile.attribute.visitor.AttributeVisitor;
26 import proguard.classfile.util.*;
27 import proguard.classfile.visitor.*;
28 
29 import java.io.PrintStream;
30 
31 
32 /**
33  * This ClassVisitor     and MemberVisitor prints out the reasons why
34  * classes and class members have been marked as being used.
35  *
36  * @see UsageMarker
37  *
38  * @author Eric Lafortune
39  */
40 public class ShortestUsagePrinter
41 extends      SimplifiedVisitor
42 implements   ClassVisitor,
43              MemberVisitor,
44              AttributeVisitor
45 {
46     private final ShortestUsageMarker shortestUsageMarker;
47     private final boolean             verbose;
48     private final PrintStream         ps;
49 
50 
51     /**
52      * Creates a new UsagePrinter that prints verbosely to <code>System.out</code>.
53      * @param shortestUsageMarker the usage marker that was used to mark the
54      *                            classes and class members.
55      */
ShortestUsagePrinter(ShortestUsageMarker shortestUsageMarker)56     public ShortestUsagePrinter(ShortestUsageMarker shortestUsageMarker)
57     {
58         this(shortestUsageMarker, true);
59     }
60 
61 
62     /**
63      * Creates a new UsagePrinter that prints to the given stream.
64      * @param shortestUsageMarker the usage marker that was used to mark the
65      *                            classes and class members.
66      * @param verbose             specifies whether the output should be verbose.
67      */
ShortestUsagePrinter(ShortestUsageMarker shortestUsageMarker, boolean verbose)68     public ShortestUsagePrinter(ShortestUsageMarker shortestUsageMarker,
69                                 boolean             verbose)
70     {
71         this(shortestUsageMarker, verbose, System.out);
72     }
73 
74     /**
75      * Creates a new UsagePrinter that prints to the given stream.
76      * @param shortestUsageMarker the usage marker that was used to mark the
77      *                            classes and class members.
78      * @param verbose             specifies whether the output should be verbose.
79      * @param printStream         the stream to which to print.
80      */
ShortestUsagePrinter(ShortestUsageMarker shortestUsageMarker, boolean verbose, PrintStream printStream)81     public ShortestUsagePrinter(ShortestUsageMarker shortestUsageMarker,
82                                 boolean             verbose,
83                                 PrintStream         printStream)
84     {
85         this.shortestUsageMarker = shortestUsageMarker;
86         this.verbose             = verbose;
87         this.ps                  = printStream;
88     }
89 
90 
91     // Implementations for ClassVisitor.
92 
visitProgramClass(ProgramClass programClass)93     public void visitProgramClass(ProgramClass programClass)
94     {
95         // Print the name of this class.
96         ps.println(ClassUtil.externalClassName(programClass.getName()));
97 
98         // Print the reason for keeping this class.
99         printReason(programClass);
100     }
101 
102 
visitLibraryClass(LibraryClass libraryClass)103     public void visitLibraryClass(LibraryClass libraryClass)
104     {
105         // Print the name of this class.
106         ps.println(ClassUtil.externalClassName(libraryClass.getName()));
107 
108         // Print the reason for keeping this class.
109         ps.println("  is a library class.\n");
110     }
111 
112 
113     // Implementations for MemberVisitor.
114 
visitProgramField(ProgramClass programClass, ProgramField programField)115     public void visitProgramField(ProgramClass programClass, ProgramField programField)
116     {
117         // Print the name of this field.
118         String name = programField.getName(programClass);
119         String type = programField.getDescriptor(programClass);
120 
121         ps.println(ClassUtil.externalClassName(programClass.getName()) +
122                    (verbose ?
123                         ": " + ClassUtil.externalFullFieldDescription(0, name, type):
124                         "."  + name));
125 
126         // Print the reason for keeping this method.
127         printReason(programField);
128     }
129 
130 
visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)131     public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
132     {
133         // Print the name of this method.
134         String name = programMethod.getName(programClass);
135         String type = programMethod.getDescriptor(programClass);
136 
137         ps.print(ClassUtil.externalClassName(programClass.getName()) +
138                  (verbose ?
139                       ": " + ClassUtil.externalFullMethodDescription(programClass.getName(), 0, name, type):
140                       "."  + name));
141         programMethod.attributesAccept(programClass, this);
142         ps.println();
143 
144         // Print the reason for keeping this method.
145         printReason(programMethod);
146     }
147 
148 
visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)149     public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
150     {
151         // Print the name of this field.
152         String name = libraryField.getName(libraryClass);
153         String type = libraryField.getDescriptor(libraryClass);
154 
155         ps.println(ClassUtil.externalClassName(libraryClass.getName()) +
156                    (verbose ?
157                         ": " + ClassUtil.externalFullFieldDescription(0, name, type):
158                         "."  + name));
159 
160         // Print the reason for keeping this field.
161         ps.println("  is a library field.\n");
162     }
163 
164 
visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)165     public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
166     {
167         // Print the name of this method.
168         String name = libraryMethod.getName(libraryClass);
169         String type = libraryMethod.getDescriptor(libraryClass);
170 
171         ps.println(ClassUtil.externalClassName(libraryClass.getName()) +
172                    (verbose ?
173                         ": " + ClassUtil.externalFullMethodDescription(libraryClass.getName(), 0, name, type):
174                         "."  + name));
175 
176         // Print the reason for keeping this method.
177         ps.println("  is a library method.\n");
178     }
179 
180 
181     // Implementations for AttributeVisitor.
182 
visitAnyAttribute(Clazz clazz, Attribute attribute)183     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
184 
185 
visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)186     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
187     {
188         codeAttribute.attributesAccept(clazz, method, this);
189     }
190 
191 
visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)192     public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)
193     {
194         ps.print(" (" +
195                  lineNumberTableAttribute.getLowestLineNumber() + ":" +
196                  lineNumberTableAttribute.getHighestLineNumber() + ")");
197     }
198 
199 
200     // Small utility methods.
201 
printReason(VisitorAccepter visitorAccepter)202     private void printReason(VisitorAccepter visitorAccepter)
203     {
204         if (shortestUsageMarker.isUsed(visitorAccepter))
205         {
206             ShortestUsageMark shortestUsageMark = shortestUsageMarker.getShortestUsageMark(visitorAccepter);
207 
208             // Print the reason for keeping this class.
209             ps.print("  " + shortestUsageMark.getReason());
210 
211             // Print the class or method that is responsible, with its reasons.
212             shortestUsageMark.acceptClassVisitor(this);
213             shortestUsageMark.acceptMemberVisitor(this);
214         }
215         else
216         {
217             ps.println("  is not being kept.\n");
218         }
219     }
220 }
221