/*
|
* 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;
|
}
|
}
|