Shrinker.java revision b9cc48a43ed984587c939d02fba5316bf5c0df6e
1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2013 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 */
21package proguard.shrink;
22
23import proguard.*;
24import proguard.classfile.*;
25import proguard.classfile.attribute.visitor.*;
26import proguard.classfile.visitor.*;
27
28import java.io.*;
29
30/**
31 * This class shrinks class pools according to a given configuration.
32 *
33 * @author Eric Lafortune
34 */
35public class Shrinker
36{
37    private final Configuration configuration;
38
39
40    /**
41     * Creates a new Shrinker.
42     */
43    public Shrinker(Configuration configuration)
44    {
45        this.configuration = configuration;
46    }
47
48
49    /**
50     * Performs shrinking of the given program class pool.
51     */
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        // Automatically mark the parameterless constructors of seed classes,
71        // mainly for convenience and for backward compatibility.
72        ClassVisitor classUsageMarker =
73            new MultiClassVisitor(new ClassVisitor[]
74            {
75                usageMarker,
76                new NamedMethodVisitor(ClassConstants.INTERNAL_METHOD_NAME_INIT,
77                                       ClassConstants.INTERNAL_METHOD_TYPE_INIT,
78                                       usageMarker)
79            });
80
81        ClassPoolVisitor classPoolvisitor =
82            ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.keep,
83                                                                    classUsageMarker,
84                                                                    usageMarker,
85                                                                    true,
86                                                                    false,
87                                                                    false);
88        // Mark the seeds.
89        programClassPool.accept(classPoolvisitor);
90        libraryClassPool.accept(classPoolvisitor);
91
92        // Mark interfaces that have to be kept.
93        programClassPool.classesAccept(new InterfaceUsageMarker(usageMarker));
94
95        // Mark the inner class and annotation information that has to be kept.
96        programClassPool.classesAccept(
97            new UsedClassFilter(usageMarker,
98            new AllAttributeVisitor(true,
99            new MultiAttributeVisitor(new AttributeVisitor[]
100            {
101                new InnerUsageMarker(usageMarker),
102                new AnnotationUsageMarker(usageMarker),
103                new SignatureUsageMarker(usageMarker),
104                new LocalVariableTypeUsageMarker(usageMarker)
105            }))));
106
107        // Should we explain ourselves?
108        if (configuration.whyAreYouKeeping != null)
109        {
110            System.out.println();
111
112            // Create a visitor for explaining classes and class members.
113            ShortestUsagePrinter shortestUsagePrinter =
114                new ShortestUsagePrinter((ShortestUsageMarker)usageMarker,
115                                         configuration.verbose);
116
117            ClassPoolVisitor whyClassPoolvisitor =
118                ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.whyAreYouKeeping,
119                                                                        shortestUsagePrinter,
120                                                                        shortestUsagePrinter);
121
122            // Mark the seeds.
123            programClassPool.accept(whyClassPoolvisitor);
124            libraryClassPool.accept(whyClassPoolvisitor);
125        }
126
127        if (configuration.printUsage != null)
128        {
129            PrintStream ps =
130                configuration.printUsage == Configuration.STD_OUT ? System.out :
131                    new PrintStream(
132                    new BufferedOutputStream(
133                    new FileOutputStream(configuration.printUsage)));
134
135            // Print out items that will be removed.
136            programClassPool.classesAcceptAlphabetically(
137                new UsagePrinter(usageMarker, true, ps));
138
139            if (ps == System.out)
140            {
141                ps.flush();
142            }
143            else
144            {
145                ps.close();
146            }
147        }
148
149        // Discard unused program classes.
150        int originalProgramClassPoolSize = programClassPool.size();
151
152        ClassPool newProgramClassPool = new ClassPool();
153        programClassPool.classesAccept(
154            new UsedClassFilter(usageMarker,
155            new MultiClassVisitor(
156            new ClassVisitor[] {
157                new ClassShrinker(usageMarker),
158                new ClassPoolFiller(newProgramClassPool)
159            })));
160
161        programClassPool.clear();
162
163        // Check if we have at least some output classes.
164        int newProgramClassPoolSize = newProgramClassPool.size();
165        if (newProgramClassPoolSize == 0)
166        {
167            throw new IOException("The output jar is empty. Did you specify the proper '-keep' options?");
168        }
169
170        if (configuration.verbose)
171        {
172            System.out.println("Removing unused program classes and class elements...");
173            System.out.println("  Original number of program classes: " + originalProgramClassPoolSize);
174            System.out.println("  Final number of program classes:    " + newProgramClassPoolSize);
175        }
176
177        return newProgramClassPool;
178    }
179}
180