• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
3  *             of Java bytecode.
4  *
5  * Copyright (c) 2002-2009 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.*;
24 import proguard.classfile.ClassPool;
25 import proguard.classfile.attribute.visitor.*;
26 import proguard.classfile.visitor.*;
27 
28 import java.io.*;
29 
30 /**
31  * This class shrinks class pools according to a given configuration.
32  *
33  * @author Eric Lafortune
34  */
35 public class Shrinker
36 {
37     private final Configuration configuration;
38 
39 
40     /**
41      * Creates a new Shrinker.
42      */
Shrinker(Configuration configuration)43     public Shrinker(Configuration configuration)
44     {
45         this.configuration = configuration;
46     }
47 
48 
49     /**
50      * Performs shrinking of the given program class pool.
51      */
execute(ClassPool programClassPool, ClassPool libraryClassPool)52     public ClassPool execute(ClassPool programClassPool,
53                              ClassPool libraryClassPool) throws IOException
54     {
55         // Check if we have at least some keep commands.
56         if (configuration.keep == null)
57         {
58             throw new IOException("You have to specify '-keep' options for the shrinking step.");
59         }
60 
61         // Clean up any old visitor info.
62         programClassPool.classesAccept(new ClassCleaner());
63         libraryClassPool.classesAccept(new ClassCleaner());
64 
65         // Create a visitor for marking the seeds.
66         UsageMarker usageMarker = configuration.whyAreYouKeeping == null ?
67             new UsageMarker() :
68             new ShortestUsageMarker();
69 
70         ClassPoolVisitor classPoolvisitor =
71             ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
72                                                                     usageMarker,
73                                                                     usageMarker,
74                                                                     true,
75                                                                     false,
76                                                                     false);
77         // Mark the seeds.
78         programClassPool.accept(classPoolvisitor);
79         libraryClassPool.accept(classPoolvisitor);
80 
81         // Mark interfaces that have to be kept.
82         programClassPool.classesAccept(new InterfaceUsageMarker(usageMarker));
83 
84         // Mark the inner class and annotation information that has to be kept.
85         programClassPool.classesAccept(
86             new UsedClassFilter(usageMarker,
87             new AllAttributeVisitor(true,
88             new MultiAttributeVisitor(new AttributeVisitor[]
89             {
90                 new InnerUsageMarker(usageMarker),
91                 new AnnotationUsageMarker(usageMarker),
92             }))));
93 
94         // Should we explain ourselves?
95         if (configuration.whyAreYouKeeping != null)
96         {
97             System.out.println();
98 
99             // Create a visitor for explaining classes and class members.
100             ShortestUsagePrinter shortestUsagePrinter =
101                 new ShortestUsagePrinter((ShortestUsageMarker)usageMarker,
102                                          configuration.verbose);
103 
104             ClassPoolVisitor whyClassPoolvisitor =
105                 ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.whyAreYouKeeping,
106                                                                         shortestUsagePrinter,
107                                                                         shortestUsagePrinter);
108 
109             // Mark the seeds.
110             programClassPool.accept(whyClassPoolvisitor);
111             libraryClassPool.accept(whyClassPoolvisitor);
112         }
113 
114         if (configuration.printUsage != null)
115         {
116             PrintStream ps = isFile(configuration.printUsage) ?
117                 new PrintStream(new BufferedOutputStream(new FileOutputStream(configuration.printUsage))) :
118                 System.out;
119 
120             // Print out items that will be removed.
121             programClassPool.classesAcceptAlphabetically(
122                 new UsagePrinter(usageMarker, true, ps));
123 
124             if (ps != System.out)
125             {
126                 ps.close();
127             }
128         }
129 
130         // Discard unused program classes.
131         int originalProgramClassPoolSize = programClassPool.size();
132 
133         ClassPool newProgramClassPool = new ClassPool();
134         programClassPool.classesAccept(
135             new UsedClassFilter(usageMarker,
136             new MultiClassVisitor(
137             new ClassVisitor[] {
138                 new ClassShrinker(usageMarker),
139                 new ClassPoolFiller(newProgramClassPool)
140             })));
141 
142         programClassPool.clear();
143 
144         // Check if we have at least some output classes.
145         int newProgramClassPoolSize = newProgramClassPool.size();
146         if (newProgramClassPoolSize == 0)
147         {
148             throw new IOException("The output jar is empty. Did you specify the proper '-keep' options?");
149         }
150 
151         if (configuration.verbose)
152         {
153             System.out.println("Removing unused program classes and class elements...");
154             System.out.println("  Original number of program classes: " + originalProgramClassPoolSize);
155             System.out.println("  Final number of program classes:    " + newProgramClassPoolSize);
156         }
157 
158         return newProgramClassPool;
159     }
160 
161 
162     /**
163      * Returns whether the given file is actually a file, or just a placeholder
164      * for the standard output.
165      */
isFile(File file)166     private boolean isFile(File file)
167     {
168         return file.getPath().length() > 0;
169     }
170 }
171