diff options
Diffstat (limited to 'src/proguard/Initializer.java')
-rw-r--r-- | src/proguard/Initializer.java | 411 |
1 files changed, 411 insertions, 0 deletions
diff --git a/src/proguard/Initializer.java b/src/proguard/Initializer.java new file mode 100644 index 0000000..41cf971 --- /dev/null +++ b/src/proguard/Initializer.java @@ -0,0 +1,411 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard; + +import proguard.classfile.ClassPool; +import proguard.classfile.attribute.visitor.AllAttributeVisitor; +import proguard.classfile.constant.visitor.*; +import proguard.classfile.instruction.visitor.AllInstructionVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; +import proguard.util.*; + +import java.io.IOException; +import java.util.*; + +/** + * This class initializes class pools. + * + * @author Eric Lafortune + */ +public class Initializer +{ + private final Configuration configuration; + + + /** + * Creates a new Initializer to initialize classes according to the given + * configuration. + */ + public Initializer(Configuration configuration) + { + this.configuration = configuration; + } + + + /** + * Initializes the classes in the given program class pool and library class + * pool, performs some basic checks, and shrinks the library class pool. + */ + public void execute(ClassPool programClassPool, + ClassPool libraryClassPool) throws IOException + { + int originalLibraryClassPoolSize = libraryClassPool.size(); + + // Construct a reduced library class pool with only those library + // classes whose hierarchies are referenced by the program classes. + // We can't do this if we later have to come up with the obfuscated + // class member names that are globally unique. + ClassPool reducedLibraryClassPool = configuration.useUniqueClassMemberNames ? + null : new ClassPool(); + + WarningPrinter classReferenceWarningPrinter = new WarningPrinter(System.err, configuration.warn); + WarningPrinter dependencyWarningPrinter = new WarningPrinter(System.err, configuration.warn); + + // Initialize the superclass hierarchies for program classes. + programClassPool.classesAccept( + new ClassSuperHierarchyInitializer(programClassPool, + libraryClassPool, + classReferenceWarningPrinter, + null)); + + // Initialize the superclass hierarchy of all library classes, without + // warnings. + libraryClassPool.classesAccept( + new ClassSuperHierarchyInitializer(programClassPool, + libraryClassPool, + null, + dependencyWarningPrinter)); + + // Initialize the class references of program class members and + // attributes. Note that all superclass hierarchies have to be + // initialized for this purpose. + WarningPrinter memberReferenceWarningPrinter = new WarningPrinter(System.err, configuration.warn); + + programClassPool.classesAccept( + new ClassReferenceInitializer(programClassPool, + libraryClassPool, + classReferenceWarningPrinter, + memberReferenceWarningPrinter, + null)); + + if (reducedLibraryClassPool != null) + { + // Collect the library classes that are directly referenced by + // program classes, without introspection. + programClassPool.classesAccept( + new ReferencedClassVisitor( + new LibraryClassFilter( + new ClassPoolFiller(reducedLibraryClassPool)))); + + // Reinitialize the superclass hierarchies of referenced library + // classes, this time with warnings. + reducedLibraryClassPool.classesAccept( + new ClassSuperHierarchyInitializer(programClassPool, + libraryClassPool, + classReferenceWarningPrinter, + null)); + } + + // Initialize the Class.forName references. + WarningPrinter dynamicClassReferenceNotePrinter = new WarningPrinter(System.out, configuration.note); + WarningPrinter classForNameNotePrinter = new WarningPrinter(System.out, configuration.note); + + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new AllInstructionVisitor( + new DynamicClassReferenceInitializer(programClassPool, + libraryClassPool, + dynamicClassReferenceNotePrinter, + null, + classForNameNotePrinter, + createClassNoteExceptionMatcher(configuration.keep)))))); + + // Initialize the Class.get[Declared]{Field,Method} references. + WarningPrinter getMemberNotePrinter = new WarningPrinter(System.out, configuration.note); + + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new AllInstructionVisitor( + new DynamicMemberReferenceInitializer(programClassPool, + libraryClassPool, + getMemberNotePrinter, + createClassMemberNoteExceptionMatcher(configuration.keep, true), + createClassMemberNoteExceptionMatcher(configuration.keep, false)))))); + + // Initialize other string constant references, if requested. + if (configuration.adaptClassStrings != null) + { + programClassPool.classesAccept( + new ClassNameFilter(configuration.adaptClassStrings, + new AllConstantVisitor( + new StringReferenceInitializer(programClassPool, + libraryClassPool)))); + } + + // Print various notes, if specified. + WarningPrinter fullyQualifiedClassNameNotePrinter = new WarningPrinter(System.out, configuration.note); + WarningPrinter descriptorKeepNotePrinter = new WarningPrinter(System.out, configuration.note); + + new FullyQualifiedClassNameChecker(programClassPool, + libraryClassPool, + fullyQualifiedClassNameNotePrinter).checkClassSpecifications(configuration.keep); + + new DescriptorKeepChecker(programClassPool, + libraryClassPool, + descriptorKeepNotePrinter).checkClassSpecifications(configuration.keep); + + // Initialize the class references of library class members. + if (reducedLibraryClassPool != null) + { + // Collect the library classes that are referenced by program + // classes, directly or indirectly, with or without introspection. + programClassPool.classesAccept( + new ReferencedClassVisitor( + new LibraryClassFilter( + new ClassHierarchyTraveler(true, true, true, false, + new LibraryClassFilter( + new ClassPoolFiller(reducedLibraryClassPool)))))); + + // Initialize the class references of referenced library + // classes, without warnings. + reducedLibraryClassPool.classesAccept( + new ClassReferenceInitializer(programClassPool, + libraryClassPool, + null, + null, + dependencyWarningPrinter)); + + // Reset the library class pool. + libraryClassPool.clear(); + + // Copy the library classes that are referenced directly by program + // classes and the library classes that are referenced by referenced + // library classes. + reducedLibraryClassPool.classesAccept( + new MultiClassVisitor(new ClassVisitor[] + { + new ClassHierarchyTraveler(true, true, true, false, + new LibraryClassFilter( + new ClassPoolFiller(libraryClassPool))), + + new ReferencedClassVisitor( + new LibraryClassFilter( + new ClassHierarchyTraveler(true, true, true, false, + new LibraryClassFilter( + new ClassPoolFiller(libraryClassPool))))) + })); + } + else + { + // Initialize the class references of all library class members. + libraryClassPool.classesAccept( + new ClassReferenceInitializer(programClassPool, + libraryClassPool, + null, + null, + dependencyWarningPrinter)); + } + + // Initialize the subclass hierarchies. + programClassPool.classesAccept(new ClassSubHierarchyInitializer()); + libraryClassPool.classesAccept(new ClassSubHierarchyInitializer()); + + // Share strings between the classes, to reduce heap memory usage. + programClassPool.classesAccept(new StringSharer()); + libraryClassPool.classesAccept(new StringSharer()); + + // Print out a summary of the notes, if necessary. + int fullyQualifiedNoteCount = fullyQualifiedClassNameNotePrinter.getWarningCount(); + if (fullyQualifiedNoteCount > 0) + { + System.out.println("Note: there were " + fullyQualifiedNoteCount + + " references to unknown classes."); + System.out.println(" You should check your configuration for typos."); + } + + int descriptorNoteCount = descriptorKeepNotePrinter.getWarningCount(); + if (descriptorNoteCount > 0) + { + System.out.println("Note: there were " + descriptorNoteCount + + " unkept descriptor classes in kept class members."); + System.out.println(" You should consider explicitly keeping the mentioned classes"); + System.out.println(" (using '-keep')."); + } + + int dynamicClassReferenceNoteCount = dynamicClassReferenceNotePrinter.getWarningCount(); + if (dynamicClassReferenceNoteCount > 0) + { + System.out.println("Note: there were " + dynamicClassReferenceNoteCount + + " unresolved dynamic references to classes or interfaces."); + System.err.println(" You should check if you need to specify additional program jars."); + } + + int classForNameNoteCount = classForNameNotePrinter.getWarningCount(); + if (classForNameNoteCount > 0) + { + System.out.println("Note: there were " + classForNameNoteCount + + " class casts of dynamically created class instances."); + System.out.println(" You might consider explicitly keeping the mentioned classes and/or"); + System.out.println(" their implementations (using '-keep')."); + } + + int getmemberNoteCount = getMemberNotePrinter.getWarningCount(); + if (getmemberNoteCount > 0) + { + System.out.println("Note: there were " + getmemberNoteCount + + " accesses to class members by means of introspection."); + System.out.println(" You should consider explicitly keeping the mentioned class members"); + System.out.println(" (using '-keep' or '-keepclassmembers')."); + } + + // Print out a summary of the warnings, if necessary. + int classReferenceWarningCount = classReferenceWarningPrinter.getWarningCount(); + if (classReferenceWarningCount > 0) + { + System.err.println("Warning: there were " + classReferenceWarningCount + + " unresolved references to classes or interfaces."); + System.err.println(" You may need to specify additional library jars (using '-libraryjars'),"); + System.err.println(" or perhaps the '-dontskipnonpubliclibraryclasses' option."); + } + + int dependencyWarningCount = dependencyWarningPrinter.getWarningCount(); + if (dependencyWarningCount > 0) + { + System.err.println("Warning: there were " + dependencyWarningCount + + " instances of library classes depending on program classes."); + System.err.println(" You must avoid such dependencies, since the program classes will"); + System.err.println(" be processed, while the library classes will remain unchanged."); + } + + int memberReferenceWarningCount = memberReferenceWarningPrinter.getWarningCount(); + if (memberReferenceWarningCount > 0) + { + System.err.println("Warning: there were " + memberReferenceWarningCount + + " unresolved references to program class members."); + System.err.println(" Your input classes appear to be inconsistent."); + System.err.println(" You may need to recompile them and try again."); + System.err.println(" Alternatively, you may have to specify the options "); + System.err.println(" '-dontskipnonpubliclibraryclasses' and/or"); + System.err.println(" '-dontskipnonpubliclibraryclassmembers'."); + } + + if ((classReferenceWarningCount > 0 || + dependencyWarningCount > 0 || + memberReferenceWarningCount > 0) && + !configuration.ignoreWarnings) + { + throw new IOException("Please correct the above warnings first."); + } + + if ((configuration.note == null || + !configuration.note.isEmpty()) && + (configuration.warn != null && + configuration.warn.isEmpty() || + configuration.ignoreWarnings)) + { + System.out.println("Note: You're ignoring all warnings!"); + } + + // Discard unused library classes. + if (configuration.verbose) + { + System.out.println("Ignoring unused library classes..."); + System.out.println(" Original number of library classes: " + originalLibraryClassPoolSize); + System.out.println(" Final number of library classes: " + libraryClassPool.size()); + } + } + + + /** + * Extracts a list of exceptions of classes for which not to print notes, + * from the keep configuration. + */ + private StringMatcher createClassNoteExceptionMatcher(List noteExceptions) + { + if (noteExceptions != null) + { + List noteExceptionNames = new ArrayList(noteExceptions.size()); + for (int index = 0; index < noteExceptions.size(); index++) + { + KeepClassSpecification keepClassSpecification = (KeepClassSpecification)noteExceptions.get(index); + if (keepClassSpecification.markClasses) + { + // If the class itself is being kept, it's ok. + String className = keepClassSpecification.className; + if (className != null) + { + noteExceptionNames.add(className); + } + + // If all of its extensions are being kept, it's ok too. + String extendsClassName = keepClassSpecification.extendsClassName; + if (extendsClassName != null) + { + noteExceptionNames.add(extendsClassName); + } + } + } + + if (noteExceptionNames.size() > 0) + { + return new ListParser(new ClassNameParser()).parse(noteExceptionNames); + } + } + + return null; + } + + + /** + * Extracts a list of exceptions of field or method names for which not to + * print notes, from the keep configuration. + */ + private StringMatcher createClassMemberNoteExceptionMatcher(List noteExceptions, + boolean isField) + { + if (noteExceptions != null) + { + List noteExceptionNames = new ArrayList(); + for (int index = 0; index < noteExceptions.size(); index++) + { + KeepClassSpecification keepClassSpecification = (KeepClassSpecification)noteExceptions.get(index); + List memberSpecifications = isField ? + keepClassSpecification.fieldSpecifications : + keepClassSpecification.methodSpecifications; + + if (memberSpecifications != null) + { + for (int index2 = 0; index2 < memberSpecifications.size(); index2++) + { + MemberSpecification memberSpecification = + (MemberSpecification)memberSpecifications.get(index2); + + String memberName = memberSpecification.name; + if (memberName != null) + { + noteExceptionNames.add(memberName); + } + } + } + } + + if (noteExceptionNames.size() > 0) + { + return new ListParser(new ClassNameParser()).parse(noteExceptionNames); + } + } + + return null; + } +} |