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; 22 23import proguard.classfile.ClassPool; 24import proguard.classfile.editor.ClassElementSorter; 25import proguard.classfile.visitor.*; 26import proguard.obfuscate.Obfuscator; 27import proguard.optimize.*; 28import proguard.preverify.*; 29import proguard.shrink.Shrinker; 30 31import java.io.*; 32import java.util.Properties; 33 34/** 35 * Tool for shrinking, optimizing, obfuscating, and preverifying Java classes. 36 * 37 * @author Eric Lafortune 38 */ 39public class ProGuard 40{ 41 public static final String VERSION = "ProGuard, version 4.10"; 42 43 private final Configuration configuration; 44 private ClassPool programClassPool = new ClassPool(); 45 private final ClassPool libraryClassPool = new ClassPool(); 46 47 48 /** 49 * Creates a new ProGuard object to process jars as specified by the given 50 * configuration. 51 */ 52 public ProGuard(Configuration configuration) 53 { 54 this.configuration = configuration; 55 } 56 57 58 /** 59 * Performs all subsequent ProGuard operations. 60 */ 61 public void execute() throws IOException 62 { 63 System.out.println(VERSION); 64 65 GPL.check(); 66 67 if (configuration.printConfiguration != null) 68 { 69 printConfiguration(); 70 } 71 72 if (configuration.programJars != null && 73 configuration.programJars.hasOutput() && 74 new UpToDateChecker(configuration).check()) 75 { 76 return; 77 } 78 79 readInput(); 80 81 if (configuration.printSeeds != null || 82 configuration.shrink || 83 configuration.optimize || 84 configuration.obfuscate || 85 configuration.preverify) 86 { 87 initialize(); 88 } 89 90 if (configuration.targetClassVersion != 0) 91 { 92 target(); 93 } 94 95 if (configuration.printSeeds != null) 96 { 97 printSeeds(); 98 } 99 100 if (configuration.shrink) 101 { 102 shrink(); 103 } 104 105 if (configuration.preverify) 106 { 107 inlineSubroutines(); 108 } 109 110 if (configuration.optimize) 111 { 112 for (int optimizationPass = 0; 113 optimizationPass < configuration.optimizationPasses; 114 optimizationPass++) 115 { 116 if (!optimize()) 117 { 118 // Stop optimizing if the code doesn't improve any further. 119 break; 120 } 121 122 // Shrink again, if we may. 123 if (configuration.shrink) 124 { 125 // Don't print any usage this time around. 126 configuration.printUsage = null; 127 configuration.whyAreYouKeeping = null; 128 129 shrink(); 130 } 131 } 132 } 133 134 if (configuration.obfuscate) 135 { 136 obfuscate(); 137 } 138 139 if (configuration.preverify) 140 { 141 preverify(); 142 } 143 144 if (configuration.shrink || 145 configuration.optimize || 146 configuration.obfuscate || 147 configuration.preverify) 148 { 149 sortClassElements(); 150 } 151 152 if (configuration.programJars.hasOutput()) 153 { 154 writeOutput(); 155 } 156 157 if (configuration.dump != null) 158 { 159 dump(); 160 } 161 } 162 163 164 /** 165 * Prints out the configuration that ProGuard is using. 166 */ 167 private void printConfiguration() throws IOException 168 { 169 if (configuration.verbose) 170 { 171 System.out.println("Printing configuration to [" + fileName(configuration.printConfiguration) + "]..."); 172 } 173 174 PrintStream ps = createPrintStream(configuration.printConfiguration); 175 try 176 { 177 new ConfigurationWriter(ps).write(configuration); 178 } 179 finally 180 { 181 closePrintStream(ps); 182 } 183 } 184 185 186 /** 187 * Reads the input class files. 188 */ 189 private void readInput() throws IOException 190 { 191 if (configuration.verbose) 192 { 193 System.out.println("Reading input..."); 194 } 195 196 // Fill the program class pool and the library class pool. 197 new InputReader(configuration).execute(programClassPool, libraryClassPool); 198 } 199 200 201 /** 202 * Initializes the cross-references between all classes, performs some 203 * basic checks, and shrinks the library class pool. 204 */ 205 private void initialize() throws IOException 206 { 207 if (configuration.verbose) 208 { 209 System.out.println("Initializing..."); 210 } 211 212 new Initializer(configuration).execute(programClassPool, libraryClassPool); 213 } 214 215 216 /** 217 * Sets that target versions of the program classes. 218 */ 219 private void target() throws IOException 220 { 221 if (configuration.verbose) 222 { 223 System.out.println("Setting target versions..."); 224 } 225 226 new Targeter(configuration).execute(programClassPool); 227 } 228 229 230 /** 231 * Prints out classes and class members that are used as seeds in the 232 * shrinking and obfuscation steps. 233 */ 234 private void printSeeds() throws IOException 235 { 236 if (configuration.verbose) 237 { 238 System.out.println("Printing kept classes, fields, and methods..."); 239 } 240 241 PrintStream ps = createPrintStream(configuration.printSeeds); 242 try 243 { 244 new SeedPrinter(ps).write(configuration, programClassPool, libraryClassPool); 245 } 246 finally 247 { 248 closePrintStream(ps); 249 } 250 } 251 252 253 /** 254 * Performs the shrinking step. 255 */ 256 private void shrink() throws IOException 257 { 258 if (configuration.verbose) 259 { 260 System.out.println("Shrinking..."); 261 262 // We'll print out some explanation, if requested. 263 if (configuration.whyAreYouKeeping != null) 264 { 265 System.out.println("Explaining why classes and class members are being kept..."); 266 } 267 268 // We'll print out the usage, if requested. 269 if (configuration.printUsage != null) 270 { 271 System.out.println("Printing usage to [" + fileName(configuration.printUsage) + "]..."); 272 } 273 } 274 275 // Perform the actual shrinking. 276 programClassPool = 277 new Shrinker(configuration).execute(programClassPool, libraryClassPool); 278 } 279 280 281 /** 282 * Performs the subroutine inlining step. 283 */ 284 private void inlineSubroutines() 285 { 286 if (configuration.verbose) 287 { 288 System.out.println("Inlining subroutines..."); 289 } 290 291 // Perform the actual inlining. 292 new SubroutineInliner(configuration).execute(programClassPool); 293 } 294 295 296 /** 297 * Performs the optimization step. 298 */ 299 private boolean optimize() throws IOException 300 { 301 if (configuration.verbose) 302 { 303 System.out.println("Optimizing..."); 304 } 305 306 // Perform the actual optimization. 307 return new Optimizer(configuration).execute(programClassPool, libraryClassPool); 308 } 309 310 311 /** 312 * Performs the obfuscation step. 313 */ 314 private void obfuscate() throws IOException 315 { 316 if (configuration.verbose) 317 { 318 System.out.println("Obfuscating..."); 319 320 // We'll apply a mapping, if requested. 321 if (configuration.applyMapping != null) 322 { 323 System.out.println("Applying mapping [" + fileName(configuration.applyMapping) + "]"); 324 } 325 326 // We'll print out the mapping, if requested. 327 if (configuration.printMapping != null) 328 { 329 System.out.println("Printing mapping to [" + fileName(configuration.printMapping) + "]..."); 330 } 331 } 332 333 // Perform the actual obfuscation. 334 new Obfuscator(configuration).execute(programClassPool, libraryClassPool); 335 } 336 337 338 /** 339 * Performs the preverification step. 340 */ 341 private void preverify() 342 { 343 if (configuration.verbose) 344 { 345 System.out.println("Preverifying..."); 346 } 347 348 // Perform the actual preverification. 349 new Preverifier(configuration).execute(programClassPool); 350 } 351 352 353 /** 354 * Sorts the elements of all program classes. 355 */ 356 private void sortClassElements() 357 { 358 programClassPool.classesAccept(new ClassElementSorter()); 359 } 360 361 362 /** 363 * Writes the output class files. 364 */ 365 private void writeOutput() throws IOException 366 { 367 if (configuration.verbose) 368 { 369 System.out.println("Writing output..."); 370 } 371 372 // Write out the program class pool. 373 new OutputWriter(configuration).execute(programClassPool); 374 } 375 376 377 /** 378 * Prints out the contents of the program classes. 379 */ 380 private void dump() throws IOException 381 { 382 if (configuration.verbose) 383 { 384 System.out.println("Printing classes to [" + fileName(configuration.dump) + "]..."); 385 } 386 387 PrintStream ps = createPrintStream(configuration.dump); 388 try 389 { 390 programClassPool.classesAccept(new ClassPrinter(ps)); 391 } 392 finally 393 { 394 closePrintStream(ps); 395 } 396 } 397 398 399 /** 400 * Returns a print stream for the given file, or the standard output if 401 * the file name is empty. 402 */ 403 private PrintStream createPrintStream(File file) 404 throws FileNotFoundException 405 { 406 return file == Configuration.STD_OUT ? System.out : 407 new PrintStream( 408 new BufferedOutputStream( 409 new FileOutputStream(file))); 410 } 411 412 413 /** 414 * Closes the given print stream, or closes it if is the standard output. 415 * @param printStream 416 */ 417 private void closePrintStream(PrintStream printStream) 418 { 419 if (printStream == System.out) 420 { 421 printStream.flush(); 422 } 423 else 424 { 425 printStream.close(); 426 } 427 } 428 429 430 /** 431 * Returns the canonical file name for the given file, or "standard output" 432 * if the file name is empty. 433 */ 434 private String fileName(File file) 435 { 436 if (file == Configuration.STD_OUT) 437 { 438 return "standard output"; 439 } 440 else 441 { 442 try 443 { 444 return file.getCanonicalPath(); 445 } 446 catch (IOException ex) 447 { 448 return file.getPath(); 449 } 450 } 451 } 452 453 454 /** 455 * The main method for ProGuard. 456 */ 457 public static void main(String[] args) 458 { 459 if (args.length == 0) 460 { 461 System.out.println(VERSION); 462 System.out.println("Usage: java proguard.ProGuard [options ...]"); 463 System.exit(1); 464 } 465 466 // Create the default options. 467 Configuration configuration = new Configuration(); 468 469 try 470 { 471 // Parse the options specified in the command line arguments. 472 ConfigurationParser parser = new ConfigurationParser(args, 473 System.getProperties()); 474 try 475 { 476 parser.parse(configuration); 477 } 478 finally 479 { 480 parser.close(); 481 } 482 483 // Execute ProGuard with these options. 484 new ProGuard(configuration).execute(); 485 } 486 catch (Exception ex) 487 { 488 if (configuration.verbose) 489 { 490 // Print a verbose stack trace. 491 ex.printStackTrace(); 492 } 493 else 494 { 495 // Print just the stack trace message. 496 System.err.println("Error: "+ex.getMessage()); 497 } 498 499 System.exit(1); 500 } 501 502 System.exit(0); 503 } 504} 505