Shrinker.java revision cfead78069f3dc32998dc118ee08cab3867acea2
1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2011 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 })))); 104 105 // Should we explain ourselves? 106 if (configuration.whyAreYouKeeping != null) 107 { 108 System.out.println(); 109 110 // Create a visitor for explaining classes and class members. 111 ShortestUsagePrinter shortestUsagePrinter = 112 new ShortestUsagePrinter((ShortestUsageMarker)usageMarker, 113 configuration.verbose); 114 115 ClassPoolVisitor whyClassPoolvisitor = 116 ClassSpecificationVisitorFactory.createClassPoolVisitor(configuration.whyAreYouKeeping, 117 shortestUsagePrinter, 118 shortestUsagePrinter); 119 120 // Mark the seeds. 121 programClassPool.accept(whyClassPoolvisitor); 122 libraryClassPool.accept(whyClassPoolvisitor); 123 } 124 125 if (configuration.printUsage != null) 126 { 127 PrintStream ps = isFile(configuration.printUsage) ? 128 new PrintStream(new BufferedOutputStream(new FileOutputStream(configuration.printUsage))) : 129 System.out; 130 131 // Print out items that will be removed. 132 programClassPool.classesAcceptAlphabetically( 133 new UsagePrinter(usageMarker, true, ps)); 134 135 if (ps != System.out) 136 { 137 ps.close(); 138 } 139 } 140 141 // Discard unused program classes. 142 int originalProgramClassPoolSize = programClassPool.size(); 143 144 ClassPool newProgramClassPool = new ClassPool(); 145 programClassPool.classesAccept( 146 new UsedClassFilter(usageMarker, 147 new MultiClassVisitor( 148 new ClassVisitor[] { 149 new ClassShrinker(usageMarker), 150 new ClassPoolFiller(newProgramClassPool) 151 }))); 152 153 programClassPool.clear(); 154 155 // Check if we have at least some output classes. 156 int newProgramClassPoolSize = newProgramClassPool.size(); 157 if (newProgramClassPoolSize == 0) 158 { 159 throw new IOException("The output jar is empty. Did you specify the proper '-keep' options?"); 160 } 161 162 if (configuration.verbose) 163 { 164 System.out.println("Removing unused program classes and class elements..."); 165 System.out.println(" Original number of program classes: " + originalProgramClassPoolSize); 166 System.out.println(" Final number of program classes: " + newProgramClassPoolSize); 167 } 168 169 return newProgramClassPool; 170 } 171 172 173 /** 174 * Returns whether the given file is actually a file, or just a placeholder 175 * for the standard output. 176 */ 177 private boolean isFile(File file) 178 { 179 return file.getPath().length() > 0; 180 } 181} 182