/* * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * * Copyright (c) 2002-2014 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.*; import proguard.classfile.attribute.annotation.visitor.AllElementValueVisitor; import proguard.classfile.attribute.visitor.AllAttributeVisitor; import proguard.classfile.constant.visitor.AllConstantVisitor; 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(); // Perform basic checks on the configuration. WarningPrinter fullyQualifiedClassNameNotePrinter = new WarningPrinter(System.out, configuration.note); FullyQualifiedClassNameChecker fullyQualifiedClassNameChecker = new FullyQualifiedClassNameChecker(programClassPool, libraryClassPool, fullyQualifiedClassNameNotePrinter); fullyQualifiedClassNameChecker.checkClassSpecifications(configuration.keep); fullyQualifiedClassNameChecker.checkClassSpecifications(configuration.assumeNoSideEffects); StringMatcher keepAttributesMatcher = configuration.keepAttributes != null ? new ListParser(new NameParser()).parse(configuration.keepAttributes) : new EmptyStringMatcher(); WarningPrinter getAnnotationNotePrinter = new WarningPrinter(System.out, configuration.note); if (!keepAttributesMatcher.matches(ClassConstants.ATTR_RuntimeVisibleAnnotations)) { programClassPool.classesAccept( new AllConstantVisitor( new GetAnnotationChecker(getAnnotationNotePrinter))); } WarningPrinter getSignatureNotePrinter = new WarningPrinter(System.out, configuration.note); if (!keepAttributesMatcher.matches(ClassConstants.ATTR_Signature)) { programClassPool.classesAccept( new AllConstantVisitor( new GetSignatureChecker(getSignatureNotePrinter))); } WarningPrinter getEnclosingClassNotePrinter = new WarningPrinter(System.out, configuration.note); if (!keepAttributesMatcher.matches(ClassConstants.ATTR_InnerClasses)) { programClassPool.classesAccept( new AllConstantVisitor( new GetEnclosingClassChecker(getEnclosingClassNotePrinter))); } WarningPrinter getEnclosingMethodNotePrinter = new WarningPrinter(System.out, configuration.note); if (!keepAttributesMatcher.matches(ClassConstants.ATTR_EnclosingMethod)) { programClassPool.classesAccept( new AllConstantVisitor( new GetEnclosingMethodChecker(getEnclosingMethodNotePrinter))); } // 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 programMemberReferenceWarningPrinter = new WarningPrinter(System.err, configuration.warn); WarningPrinter libraryMemberReferenceWarningPrinter = new WarningPrinter(System.err, configuration.warn); programClassPool.classesAccept( new ClassReferenceInitializer(programClassPool, libraryClassPool, classReferenceWarningPrinter, programMemberReferenceWarningPrinter, libraryMemberReferenceWarningPrinter, 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 enum annotation references. programClassPool.classesAccept( new AllAttributeVisitor(true, new AllElementValueVisitor(true, new EnumFieldReferenceInitializer()))); // 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)))); } // 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 reflection. 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, 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, 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()); // Check for any unmatched class members. WarningPrinter classMemberNotePrinter = new WarningPrinter(System.out, configuration.note); ClassMemberChecker classMemberChecker = new ClassMemberChecker(programClassPool, classMemberNotePrinter); classMemberChecker.checkClassSpecifications(configuration.keep); classMemberChecker.checkClassSpecifications(configuration.assumeNoSideEffects); // Check for unkept descriptor classes of kept class members. WarningPrinter descriptorKeepNotePrinter = new WarningPrinter(System.out, configuration.note); new DescriptorKeepChecker(programClassPool, libraryClassPool, descriptorKeepNotePrinter).checkClassSpecifications(configuration.keep); // Check for keep options that only match library classes. WarningPrinter libraryKeepNotePrinter = new WarningPrinter(System.out, configuration.note); new LibraryKeepChecker(programClassPool, libraryClassPool, libraryKeepNotePrinter).checkClassSpecifications(configuration.keep); // 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."); System.out.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#unknownclass)"); } int classMemberNoteCount = classMemberNotePrinter.getWarningCount(); if (classMemberNoteCount > 0) { System.out.println("Note: there were " + classMemberNoteCount + " references to unknown class members."); System.out.println(" You should check your configuration for typos."); } int getAnnotationNoteCount = getAnnotationNotePrinter.getWarningCount(); if (getAnnotationNoteCount > 0) { System.out.println("Note: there were " + getAnnotationNoteCount + " classes trying to access annotations using reflection."); System.out.println(" You should consider keeping the annotation attributes"); System.out.println(" (using '-keepattributes *Annotation*')."); System.out.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#attributes)"); } int getSignatureNoteCount = getSignatureNotePrinter.getWarningCount(); if (getSignatureNoteCount > 0) { System.out.println("Note: there were " + getSignatureNoteCount + " classes trying to access generic signatures using reflection."); System.out.println(" You should consider keeping the signature attributes"); System.out.println(" (using '-keepattributes Signature')."); System.out.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#attributes)"); } int getEnclosingClassNoteCount = getEnclosingClassNotePrinter.getWarningCount(); if (getEnclosingClassNoteCount > 0) { System.out.println("Note: there were " + getEnclosingClassNoteCount + " classes trying to access enclosing classes using reflection."); System.out.println(" You should consider keeping the inner classes attributes"); System.out.println(" (using '-keepattributes InnerClasses')."); System.out.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#attributes)"); } int getEnclosingMethodNoteCount = getEnclosingMethodNotePrinter.getWarningCount(); if (getEnclosingMethodNoteCount > 0) { System.out.println("Note: there were " + getEnclosingMethodNoteCount + " classes trying to access enclosing methods using reflection."); System.out.println(" You should consider keeping the enclosing method attributes"); System.out.println(" (using '-keepattributes InnerClasses,EnclosingMethod')."); System.out.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#attributes)"); } 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')."); System.out.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#descriptorclass)"); } int libraryNoteCount = libraryKeepNotePrinter.getWarningCount(); if (libraryNoteCount > 0) { System.out.println("Note: there were " + libraryNoteCount + " library classes explicitly being kept."); System.out.println(" You don't need to keep library classes; they are already left unchanged."); System.out.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#libraryclass)"); } int dynamicClassReferenceNoteCount = dynamicClassReferenceNotePrinter.getWarningCount(); if (dynamicClassReferenceNoteCount > 0) { System.out.println("Note: there were " + dynamicClassReferenceNoteCount + " unresolved dynamic references to classes or interfaces."); System.out.println(" You should check if you need to specify additional program jars."); System.out.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#dynamicalclass)"); } 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')."); System.out.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#dynamicalclasscast)"); } 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')."); System.out.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#dynamicalclassmember)"); } // 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 add missing library jars or update their versions."); System.err.println(" If your code works fine without the missing classes, you can suppress"); System.err.println(" the warnings with '-dontwarn' options."); if (configuration.skipNonPublicLibraryClasses) { System.err.println(" You may also have to remove the option '-skipnonpubliclibraryclasses'."); } System.err.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#unresolvedclass)"); } 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."); System.err.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#dependency)"); } int programMemberReferenceWarningCount = programMemberReferenceWarningPrinter.getWarningCount(); if (programMemberReferenceWarningCount > 0) { System.err.println("Warning: there were " + programMemberReferenceWarningCount + " unresolved references to program class members."); System.err.println(" Your input classes appear to be inconsistent."); System.err.println(" You may need to recompile the code."); System.err.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#unresolvedprogramclassmember)"); } int libraryMemberReferenceWarningCount = libraryMemberReferenceWarningPrinter.getWarningCount(); if (libraryMemberReferenceWarningCount > 0) { System.err.println("Warning: there were " + libraryMemberReferenceWarningCount + " unresolved references to library class members."); System.err.println(" You probably need to update the library versions."); if (!configuration.skipNonPublicLibraryClassMembers) { System.err.println(" Alternatively, you may have to specify the option "); System.err.println(" '-dontskipnonpubliclibraryclassmembers'."); } if (configuration.skipNonPublicLibraryClasses) { System.err.println(" You may also have to remove the option '-skipnonpubliclibraryclasses'."); } System.err.println(" (http://proguard.sourceforge.net/manual/troubleshooting.html#unresolvedlibraryclassmember)"); } if ((classReferenceWarningCount > 0 || dependencyWarningCount > 0 || programMemberReferenceWarningCount > 0 || libraryMemberReferenceWarningCount > 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; } }