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