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