Home | History | Annotate | Download | only in shrink
      1 /*
      2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
      3  *             of Java bytecode.
      4  *
      5  * Copyright (c) 2002-2009 Eric Lafortune (eric (at) 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      */
     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         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      */
    166     private boolean isFile(File file)
    167     {
    168         return file.getPath().length() > 0;
    169     }
    170 }
    171