Home | History | Annotate | Download | only in proguard
      1 /*
      2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
      3  *             of Java bytecode.
      4  *
      5  * Copyright (c) 2002-2014 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;
     22 
     23 import proguard.classfile.*;
     24 import proguard.classfile.attribute.visitor.AllAttributeVisitor;
     25 import proguard.classfile.editor.*;
     26 import proguard.classfile.visitor.*;
     27 import proguard.obfuscate.Obfuscator;
     28 import proguard.optimize.Optimizer;
     29 import proguard.preverify.*;
     30 import proguard.shrink.Shrinker;
     31 
     32 import java.io.*;
     33 
     34 /**
     35  * Tool for shrinking, optimizing, obfuscating, and preverifying Java classes.
     36  *
     37  * @author Eric Lafortune
     38  */
     39 public 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