~ubuntu-branches/ubuntu/gutsy/proguard/gutsy

« back to all changes in this revision

Viewing changes to src/proguard/ProGuard.java

  • Committer: Bazaar Package Importer
  • Author(s): Sam Clegg
  • Date: 2007-01-13 12:27:45 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20070113122745-9nq3v1qcdr02o8xd
Tags: 3.7-1
* New upstream release
* debian/control: make Arch: all (Closes: #360115)
* use "$@" rather then $* in shell startup scripts (Closes: #364962)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: ProGuard.java,v 1.101 2005/10/22 11:53:39 eric Exp $
 
1
/* $Id: ProGuard.java,v 1.101.2.13 2006/12/11 21:57:04 eric Exp $
2
2
 *
3
3
 * ProGuard -- shrinking, optimization, and obfuscation of Java class files.
4
4
 *
5
 
 * Copyright (c) 2002-2005 Eric Lafortune (eric@graphics.cornell.edu)
 
5
 * Copyright (c) 2002-2006 Eric Lafortune (eric@graphics.cornell.edu)
6
6
 *
7
7
 * This program is free software; you can redistribute it and/or modify it
8
8
 * under the terms of the GNU General Public License as published by the Free
22
22
package proguard;
23
23
 
24
24
import proguard.classfile.ClassPool;
25
 
import proguard.classfile.attribute.AllAttrInfoVisitor;
26
25
import proguard.classfile.editor.ConstantPoolSorter;
27
 
import proguard.classfile.instruction.AllInstructionVisitor;
28
 
import proguard.classfile.util.*;
29
26
import proguard.classfile.visitor.*;
30
 
import proguard.io.*;
31
27
import proguard.obfuscate.Obfuscator;
32
28
import proguard.optimize.Optimizer;
33
29
import proguard.shrink.Shrinker;
34
 
import proguard.util.ClassNameListMatcher;
35
30
 
36
31
import java.io.*;
37
 
import java.util.*;
38
32
 
39
33
/**
40
34
 * Tool for shrinking, optimizing, and obfuscating Java class files.
43
37
 */
44
38
public class ProGuard
45
39
{
46
 
    public static final String VERSION = "ProGuard, version 3.4";
 
40
    public static final String VERSION = "ProGuard, version 3.7";
47
41
 
48
42
    private Configuration configuration;
49
43
    private ClassPool     programClassPool = new ClassPool();
134
128
 
135
129
 
136
130
    /**
137
 
     * Reads the input jars (or directories).
 
131
     * Reads the input class files.
138
132
     */
139
133
    private void readInput() throws IOException
140
134
    {
141
135
        if (configuration.verbose)
142
136
        {
143
 
            System.out.println("Reading jars...");
144
 
        }
145
 
 
146
 
        // Check if we have at least some program jars.
147
 
        if (configuration.programJars == null)
148
 
        {
149
 
            throw new IOException("The input is empty. You have to specify one or more '-injars' options.");
150
 
        }
151
 
 
152
 
        // Read the input program jars.
153
 
        readInput("Reading program ",
154
 
                  configuration.programJars,
155
 
                  createDataEntryClassPoolFiller(false));
156
 
 
157
 
        // Check if we have at least some input class files.
158
 
        if (programClassPool.size() == 0)
159
 
        {
160
 
            throw new IOException("The input doesn't contain any class files. Did you specify the proper '-injars' options?");
161
 
        }
162
 
 
163
 
        // Read all library jars.
164
 
        if (configuration.libraryJars != null)
165
 
        {
166
 
            readInput("Reading library ",
167
 
                      configuration.libraryJars,
168
 
                      createDataEntryClassPoolFiller(true));
169
 
        }
170
 
    }
171
 
 
172
 
 
173
 
    /**
174
 
     * Creates a DataEntryReader that will decode class files and put them in
175
 
     * the proper class pool.
176
 
     */
177
 
    private DataEntryReader createDataEntryClassPoolFiller(boolean isLibrary)
178
 
    {
179
 
        // Get the proper class pool.
180
 
        ClassPool classPool = isLibrary ?
181
 
            libraryClassPool :
182
 
            programClassPool;
183
 
 
184
 
        // Prepare a data entry reader to filter all class files,
185
 
        // which are then decoded to class files by a class file reader,
186
 
        // which are then put in the class pool by a class pool filler.
187
 
        return
188
 
            new ClassFileFilter(
189
 
            new ClassFileReader(isLibrary,
190
 
                                configuration.skipNonPublicLibraryClasses,
191
 
                                configuration.skipNonPublicLibraryClassMembers,
192
 
                                configuration.note,
193
 
            new ClassPoolFiller(classPool, configuration.note)));
194
 
    }
195
 
 
196
 
 
197
 
    /**
198
 
     * Reads all input entries from the given class path.
199
 
     */
200
 
    private void readInput(String          messagePrefix,
201
 
                           ClassPath       classPath,
202
 
                           DataEntryReader reader) throws IOException
203
 
    {
204
 
        readInput(messagePrefix,
205
 
                  classPath,
206
 
                  0,
207
 
                  classPath.size(),
208
 
                  reader);
209
 
    }
210
 
 
211
 
 
212
 
    /**
213
 
     * Reads all input entries from the given section of the given class path.
214
 
     */
215
 
    private void readInput(String          messagePrefix,
216
 
                           ClassPath       classPath,
217
 
                           int             fromIndex,
218
 
                           int             toIndex,
219
 
                           DataEntryReader reader) throws IOException
220
 
    {
221
 
        for (int index = fromIndex; index < toIndex; index++)
222
 
        {
223
 
            ClassPathEntry entry = classPath.get(index);
224
 
            if (!entry.isOutput())
225
 
            {
226
 
                readInput(messagePrefix, entry, reader);
227
 
            }
228
 
        }
229
 
    }
230
 
 
231
 
 
232
 
    /**
233
 
     * Reads the given input class path entry.
234
 
     */
235
 
    private void readInput(String          messagePrefix,
236
 
                           ClassPathEntry  classPathEntry,
237
 
                           DataEntryReader dataEntryReader) throws IOException
238
 
    {
239
 
        try
240
 
        {
241
 
            // Create a reader that can unwrap jars, wars, ears, and zips.
242
 
            DataEntryReader reader =
243
 
                DataEntryReaderFactory.createDataEntryReader(messagePrefix,
244
 
                                                             classPathEntry,
245
 
                                                             dataEntryReader);
246
 
 
247
 
            // Create the data entry pump.
248
 
            DirectoryPump directoryPump =
249
 
                new DirectoryPump(classPathEntry.getFile());
250
 
 
251
 
            // Pump the data entries into the reader.
252
 
            directoryPump.pumpDataEntries(reader);
253
 
        }
254
 
        catch (IOException ex)
255
 
        {
256
 
            throw new IOException("Can't read [" + classPathEntry + "] (" + ex.getMessage() + ")");
257
 
        }
258
 
    }
259
 
 
260
 
 
261
 
    /**
262
 
     * Initializes the cross-references between all class files.
 
137
            System.out.println("Reading input...");
 
138
        }
 
139
 
 
140
        // Fill the program class pool and the library class pool.
 
141
        new InputReader(configuration).execute(programClassPool, libraryClassPool);
 
142
    }
 
143
 
 
144
 
 
145
    /**
 
146
     * Initializes the cross-references between all classes, performs some
 
147
     * basic checks, and shrinks the library class pool.
263
148
     */
264
149
    private void initialize() throws IOException
265
150
    {
268
153
            System.out.println("Initializing...");
269
154
        }
270
155
 
271
 
        int originalLibraryClassPoolSize = libraryClassPool.size();
272
 
 
273
 
        // Initialize the class hierarchy for program class files.
274
 
        ClassFileHierarchyInitializer classFileHierarchyInitializer =
275
 
            new ClassFileHierarchyInitializer(programClassPool,
276
 
                                              libraryClassPool,
277
 
                                              configuration.warn);
278
 
 
279
 
        programClassPool.classFilesAccept(classFileHierarchyInitializer);
280
 
 
281
 
        // Initialize the class hierarchy for library class files.
282
 
        ClassFileHierarchyInitializer classFileHierarchyInitializer2 =
283
 
            new ClassFileHierarchyInitializer(programClassPool,
284
 
                                              libraryClassPool,
285
 
                                              false);
286
 
 
287
 
        libraryClassPool.classFilesAccept(classFileHierarchyInitializer2);
288
 
 
289
 
        // Initialize the Class.forName and .class references.
290
 
        ClassFileClassForNameReferenceInitializer classFileClassForNameReferenceInitializer =
291
 
            new ClassFileClassForNameReferenceInitializer(programClassPool,
292
 
                                                          libraryClassPool,
293
 
                                                          configuration.note,
294
 
                                                          createNoteExceptionMatcher(configuration.keep));
295
 
 
296
 
        programClassPool.classFilesAccept(
297
 
            new AllMethodVisitor(
298
 
            new AllAttrInfoVisitor(
299
 
            new AllInstructionVisitor(classFileClassForNameReferenceInitializer))));
300
 
 
301
 
        // Initialize the class references from program class members and attributes.
302
 
        ClassFileReferenceInitializer classFileReferenceInitializer =
303
 
            new ClassFileReferenceInitializer(programClassPool,
304
 
                                              libraryClassPool,
305
 
                                              configuration.warn);
306
 
 
307
 
        programClassPool.classFilesAccept(classFileReferenceInitializer);
308
 
 
309
 
        // Reinitialize the library class pool with only those library classes
310
 
        // whose hierarchies are referenced by the program classes.
311
 
        ClassPool newLibraryClassPool = new ClassPool();
312
 
        programClassPool.classFilesAccept(
313
 
            new AllCpInfoVisitor(
314
 
            new ReferencedClassFileVisitor(
315
 
            new LibraryClassFileFilter(
316
 
            new ClassFileHierarchyTraveler(true, true, true, false,
317
 
            new LibraryClassFileFilter(
318
 
            new ClassPoolFiller(newLibraryClassPool, false)))))));
319
 
 
320
 
        libraryClassPool = newLibraryClassPool;
321
 
 
322
 
        // Initialize the class references from library class members.
323
 
        ClassFileReferenceInitializer classFileReferenceInitializer2 =
324
 
            new ClassFileReferenceInitializer(programClassPool,
325
 
                                              libraryClassPool,
326
 
                                              false);
327
 
 
328
 
        libraryClassPool.classFilesAccept(classFileReferenceInitializer2);
329
 
 
330
 
        int noteCount = classFileClassForNameReferenceInitializer.getNoteCount();
331
 
        if (noteCount > 0)
332
 
        {
333
 
            System.err.println("Note: there were " + noteCount +
334
 
                               " class casts of dynamically created class instances.");
335
 
            System.err.println("      You might consider explicitly keeping the mentioned classes and/or");
336
 
            System.err.println("      their implementations (using '-keep').");
337
 
        }
338
 
 
339
 
        int hierarchyWarningCount = classFileHierarchyInitializer.getWarningCount();
340
 
        if (hierarchyWarningCount > 0)
341
 
        {
342
 
            System.err.println("Warning: there were " + hierarchyWarningCount +
343
 
                               " unresolved references to superclasses or interfaces.");
344
 
            System.err.println("         You may need to specify additional library jars (using '-libraryjars'),");
345
 
            System.err.println("         or perhaps the '-dontskipnonpubliclibraryclasses' option.");
346
 
        }
347
 
 
348
 
        int referenceWarningCount = classFileReferenceInitializer.getWarningCount();
349
 
        if (referenceWarningCount > 0)
350
 
        {
351
 
            System.err.println("Warning: there were " + referenceWarningCount +
352
 
                               " unresolved references to program class members.");
353
 
            System.err.println("         Your input class files appear to be inconsistent.");
354
 
            System.err.println("         You may need to recompile them and try again.");
355
 
        }
356
 
 
357
 
        if ((hierarchyWarningCount > 0 ||
358
 
             referenceWarningCount > 0) &&
359
 
            !configuration.ignoreWarnings)
360
 
        {
361
 
            System.err.println("         If you are sure the mentioned classes are not used anyway,");
362
 
            System.err.println("         you could try your luck using the '-ignorewarnings' option.");
363
 
            throw new IOException("Please correct the above warnings first.");
364
 
        }
365
 
 
366
 
        // Discard unused library classes.
367
 
        if (configuration.verbose)
368
 
        {
369
 
            System.out.println("Removed unused library classes...");
370
 
            System.out.println("  Original number of library classes: " + originalLibraryClassPoolSize);
371
 
            System.out.println("  Final number of library classes:    " + libraryClassPool.size());
372
 
        }
373
 
    }
374
 
 
375
 
 
376
 
    /**
377
 
     * Extracts a list of exceptions for which not to print notes, from the
378
 
     * keep configuration.
379
 
     */
380
 
    private ClassNameListMatcher createNoteExceptionMatcher(List noteExceptions)
381
 
    {
382
 
        if (noteExceptions != null)
383
 
        {
384
 
            List noteExceptionNames = new ArrayList(noteExceptions.size());
385
 
            for (int index = 0; index < noteExceptions.size(); index++)
386
 
            {
387
 
                ClassSpecification classSpecification = (ClassSpecification)noteExceptions.get(index);
388
 
                if (classSpecification.markClassFiles)
389
 
                {
390
 
                    // If the class itself is being kept, it's ok.
391
 
                    String className = classSpecification.className;
392
 
                    if (className != null)
393
 
                    {
394
 
                        noteExceptionNames.add(className);
395
 
                    }
396
 
 
397
 
                    // If all of its extensions are being kept, it's ok too.
398
 
                    String extendsClassName = classSpecification.extendsClassName;
399
 
                    if (extendsClassName != null)
400
 
                    {
401
 
                        noteExceptionNames.add(extendsClassName);
402
 
                    }
403
 
                }
404
 
            }
405
 
 
406
 
            if (noteExceptionNames.size() > 0)
407
 
            {
408
 
                return new ClassNameListMatcher(noteExceptionNames);
409
 
            }
410
 
        }
411
 
 
412
 
        return null;
 
156
        new Initializer(configuration).execute(programClassPool, libraryClassPool);
413
157
    }
414
158
 
415
159
 
498
242
 
499
243
        if (configuration.verbose)
500
244
        {
501
 
            System.out.println("Removed unused program classes and class elements...");
 
245
            System.out.println("Removing unused program classes and class elements...");
502
246
            System.out.println("  Original number of program classes: " + originalProgramClassPoolSize);
503
247
            System.out.println("  Final number of program classes:    " + newProgramClassPoolSize);
504
248
        }
570
314
 
571
315
 
572
316
    /**
573
 
     * Writes the output jars.
 
317
     * Writes the output claaa files.
574
318
     */
575
319
    private void writeOutput() throws IOException
576
320
    {
577
321
        if (configuration.verbose)
578
322
        {
579
 
            System.out.println("Writing jars...");
580
 
        }
581
 
 
582
 
        ClassPath programJars = configuration.programJars;
583
 
 
584
 
        // Perform a check on the first jar.
585
 
        ClassPathEntry firstEntry = programJars.get(0);
586
 
        if (firstEntry.isOutput())
587
 
        {
588
 
            throw new IOException("The output jar [" + firstEntry.getName() +
589
 
                                  "] must be specified after an input jar, or it will be empty.");
590
 
        }
591
 
 
592
 
        // Perform some checks on the output jars.
593
 
        for (int index = 0; index < programJars.size() - 1; index++)
594
 
        {
595
 
            ClassPathEntry entry = programJars.get(index);
596
 
            if (entry.isOutput())
597
 
            {
598
 
                // Check if all but the last output jars have filters.
599
 
                if (entry.getFilter()    == null &&
600
 
                    entry.getJarFilter() == null &&
601
 
                    entry.getWarFilter() == null &&
602
 
                    entry.getEarFilter() == null &&
603
 
                    entry.getZipFilter() == null &&
604
 
                    programJars.get(index + 1).isOutput())
605
 
                {
606
 
                    throw new IOException("The output jar [" + entry.getName() +
607
 
                                          "] must have a filter, or all subsequent jars will be empty.");
608
 
                }
609
 
 
610
 
                // Check if the output jar name is different from the input jar names.
611
 
                for (int inIndex = 0; inIndex < programJars.size(); inIndex++)
612
 
                {
613
 
                    ClassPathEntry otherEntry = programJars.get(inIndex);
614
 
 
615
 
                    if (!otherEntry.isOutput() &&
616
 
                        entry.getFile().equals(otherEntry.getFile()))
617
 
                    {
618
 
                        throw new IOException("The output jar [" + entry.getName() +
619
 
                                              "] must be different from all input jars.");
620
 
                    }
621
 
                }
622
 
            }
623
 
        }
624
 
 
625
 
        int firstInputIndex = 0;
626
 
        int lastInputIndex  = 0;
627
 
 
628
 
        // Go over all program class path entries.
629
 
        for (int index = 0; index < programJars.size(); index++)
630
 
        {
631
 
            // Is it an input entry?
632
 
            ClassPathEntry entry = programJars.get(index);
633
 
            if (!entry.isOutput())
634
 
            {
635
 
                // Remember the index of the last input entry.
636
 
                lastInputIndex = index;
637
 
            }
638
 
            else
639
 
            {
640
 
                // Check if this the last output entry in a series.
641
 
                int nextIndex = index + 1;
642
 
                if (nextIndex == programJars.size() ||
643
 
                    !programJars.get(nextIndex).isOutput())
644
 
                {
645
 
                    // Write the processed input entries to the output entries.
646
 
                    writeOutput(programJars,
647
 
                                firstInputIndex,
648
 
                                lastInputIndex + 1,
649
 
                                nextIndex);
650
 
 
651
 
                    // Start with the next series of input entries.
652
 
                    firstInputIndex = nextIndex;
653
 
                }
654
 
            }
655
 
        }
656
 
    }
657
 
 
658
 
 
659
 
 
660
 
 
661
 
    /**
662
 
     * Transfers the specified input jars to the specified output jars.
663
 
     */
664
 
    private void writeOutput(ClassPath classPath,
665
 
                             int       fromInputIndex,
666
 
                             int       fromOutputIndex,
667
 
                             int       toOutputIndex)
668
 
    throws IOException
669
 
    {
670
 
        try
671
 
        {
672
 
            // Construct the writer that can write jars, wars, ears, zips, and
673
 
            // directories, cascading over the specified output entries.
674
 
            DataEntryWriter writer =
675
 
                DataEntryWriterFactory.createDataEntryWriter(classPath,
676
 
                                                             fromOutputIndex,
677
 
                                                             toOutputIndex);
678
 
 
679
 
            // Create the reader that can write class files and copy resource
680
 
            // files to the above writer.
681
 
            DataEntryReader reader =
682
 
                new ClassFileFilter(new ClassFileRewriter(programClassPool,
683
 
                                                          writer),
684
 
                                    new DataEntryCopier(writer));
685
 
 
686
 
            // Read and handle the specified input entries.
687
 
            readInput("  Copying resources from program ",
688
 
                      classPath,
689
 
                      fromInputIndex,
690
 
                      fromOutputIndex,
691
 
                      reader);
692
 
 
693
 
            // Close all output entries.
694
 
            writer.close();
695
 
        }
696
 
        catch (IOException ex)
697
 
        {
698
 
            throw new IOException("Can't write [" + classPath.get(fromOutputIndex).getName() + "] (" + ex.getMessage() + ")");
699
 
        }
 
323
            System.out.println("Writing output...");
 
324
        }
 
325
 
 
326
        // Write out the program class pool.
 
327
        new OutputWriter(configuration).execute(programClassPool);
700
328
    }
701
329
 
702
330