1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2014 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.METHOD_NAME_INIT, 77 ClassConstants.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 libraryClassPool.classesAccept(usageMarker); 92 93 // Mark interfaces that have to be kept. 94 programClassPool.classesAccept(new InterfaceUsageMarker(usageMarker)); 95 96 // Mark the inner class and annotation information that has to be kept. 97 programClassPool.classesAccept( 98 new UsedClassFilter(usageMarker, 99 new AllAttributeVisitor(true, 100 new MultiAttributeVisitor(new AttributeVisitor[] 101 { 102 new InnerUsageMarker(usageMarker), 103 new AnnotationUsageMarker(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 // Clean up used program classes and 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 // Clean up library classes. 164 libraryClassPool.classesAccept( 165 new ClassShrinker(usageMarker)); 166 167 // Check if we have at least some output classes. 168 int newProgramClassPoolSize = newProgramClassPool.size(); 169 if (newProgramClassPoolSize == 0) 170 { 171 throw new IOException("The output jar is empty. Did you specify the proper '-keep' options?"); 172 } 173 174 if (configuration.verbose) 175 { 176 System.out.println("Removing unused program classes and class elements..."); 177 System.out.println(" Original number of program classes: " + originalProgramClassPoolSize); 178 System.out.println(" Final number of program classes: " + newProgramClassPoolSize); 179 } 180 181 return newProgramClassPool; 182 } 183} 184