proguard.Initializer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of proguard-base Show documentation
Show all versions of proguard-base Show documentation
ProGuard is a free shrinker, optimizer, obfuscator, and preverifier for Java bytecode
/*
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
* Copyright (c) 2002-2022 Guardsquare NV
*
* 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 org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import proguard.classfile.*;
import proguard.classfile.attribute.Attribute;
import proguard.classfile.attribute.annotation.visitor.*;
import proguard.classfile.attribute.visitor.*;
import proguard.classfile.constant.visitor.AllConstantVisitor;
import proguard.classfile.instruction.visitor.AllInstructionVisitor;
import proguard.classfile.kotlin.visitor.ReferencedKotlinMetadataVisitor;
import proguard.classfile.util.*;
import proguard.classfile.visitor.*;
import proguard.pass.Pass;
import proguard.resources.file.visitor.ResourceJavaReferenceClassInitializer;
import proguard.resources.kotlinmodule.util.KotlinModuleReferenceInitializer;
import proguard.util.*;
import java.io.*;
import java.util.*;
/**
* This pass initializes class pools and resource information.
*
* @author Eric Lafortune
*/
public class Initializer implements Pass
{
private static final Logger logger = LogManager.getLogger(Initializer.class);
private final Configuration 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.
*/
@Override
public void execute(AppView appView) throws IOException
{
logger.info("Initializing...");
boolean checkConfiguration = configuration.shrink ||
configuration.optimize ||
configuration.obfuscate;
int originalLibraryClassPoolSize = appView.libraryClassPool.size();
// Perform basic checks on the configuration.
WarningPrinter fullyQualifiedClassNameNotePrinter = new WarningLogger(logger, configuration.note);
if (checkConfiguration)
{
FullyQualifiedClassNameChecker fullyQualifiedClassNameChecker =
new FullyQualifiedClassNameChecker(appView.programClassPool,
appView.libraryClassPool,
fullyQualifiedClassNameNotePrinter);
fullyQualifiedClassNameChecker.checkClassSpecifications(configuration.keep);
fullyQualifiedClassNameChecker.checkClassSpecifications(configuration.assumeNoSideEffects);
fullyQualifiedClassNameChecker.checkClassSpecifications(configuration.assumeNoExternalSideEffects);
fullyQualifiedClassNameChecker.checkClassSpecifications(configuration.assumeNoEscapingParameters);
fullyQualifiedClassNameChecker.checkClassSpecifications(configuration.assumeNoExternalReturnValues);
}
StringMatcher keepAttributesMatcher = configuration.keepAttributes != null ?
new ListParser(new NameParser()).parse(configuration.keepAttributes) :
new EmptyStringMatcher();
WarningPrinter getAnnotationNotePrinter = new WarningLogger(logger, configuration.note);
if (!keepAttributesMatcher.matches(Attribute.RUNTIME_VISIBLE_ANNOTATIONS))
{
appView.programClassPool.classesAccept(
new AllConstantVisitor(
new GetAnnotationChecker(getAnnotationNotePrinter)));
}
WarningPrinter getSignatureNotePrinter = new WarningLogger(logger, configuration.note);
if (!keepAttributesMatcher.matches(Attribute.SIGNATURE))
{
appView.programClassPool.classesAccept(
new AllConstantVisitor(
new GetSignatureChecker(getSignatureNotePrinter)));
}
WarningPrinter getEnclosingClassNotePrinter = new WarningLogger(logger, configuration.note);
if (!keepAttributesMatcher.matches(Attribute.INNER_CLASSES))
{
appView.programClassPool.classesAccept(
new AllConstantVisitor(
new GetEnclosingClassChecker(getEnclosingClassNotePrinter)));
}
WarningPrinter getEnclosingMethodNotePrinter = new WarningLogger(logger, configuration.note);
if (!keepAttributesMatcher.matches(Attribute.ENCLOSING_METHOD))
{
appView.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 WarningLogger(logger, configuration.warn);
WarningPrinter dependencyWarningPrinter = new WarningLogger(logger, configuration.warn);
// Initialize the superclass hierarchies for program classes.
appView.programClassPool.classesAccept(
new ClassSuperHierarchyInitializer(appView.programClassPool,
appView.libraryClassPool,
classReferenceWarningPrinter,
null));
// Initialize the superclass hierarchy of all library classes, without
// warnings.
appView.libraryClassPool.classesAccept(
new ClassSuperHierarchyInitializer(appView.programClassPool,
appView.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 WarningLogger(logger, configuration.warn);
WarningPrinter libraryMemberReferenceWarningPrinter = new WarningLogger(logger, configuration.warn);
appView.programClassPool.classesAccept(
new ClassReferenceInitializer(appView.programClassPool,
appView.libraryClassPool,
classReferenceWarningPrinter,
programMemberReferenceWarningPrinter,
libraryMemberReferenceWarningPrinter,
null));
if (reducedLibraryClassPool != null)
{
if (configuration.keepKotlinMetadata)
{
// TODO(T16917): Improve this, so that only relevant classes are kept.
appView.libraryClassPool.classesAccept(
new ReferencedKotlinMetadataVisitor(
(clazz, kotlinMetadata) -> clazz.accept(new ClassPoolFiller(reducedLibraryClassPool))
)
);
}
// Collect the library classes that are directly referenced by
// program classes, without reflection.
appView.programClassPool.classesAccept(
new ReferencedClassVisitor(true,
new LibraryClassFilter(
new ClassPoolFiller(reducedLibraryClassPool))));
// Reinitialize the superclass hierarchies of referenced library
// classes, this time with warnings.
reducedLibraryClassPool.classesAccept(
new ClassSuperHierarchyInitializer(appView.programClassPool,
appView.libraryClassPool,
classReferenceWarningPrinter,
null));
}
// Initialize the enum annotation references.
appView.programClassPool.classesAccept(
new AllAttributeVisitor(true,
new AllElementValueVisitor(true,
new EnumFieldReferenceInitializer())));
// Initialize the Class.forName references.
WarningPrinter dynamicClassReferenceNotePrinter = new WarningLogger(logger, configuration.note);
WarningPrinter classForNameNotePrinter = new WarningLogger(logger, configuration.note);
appView.programClassPool.classesAccept(
new AllMethodVisitor(
new AllAttributeVisitor(
new AllInstructionVisitor(
new DynamicClassReferenceInitializer(appView.programClassPool,
appView.libraryClassPool,
dynamicClassReferenceNotePrinter,
null,
classForNameNotePrinter,
createClassNoteExceptionMatcher(configuration.keep, true))))));
// Initialize the Class.get[Declared]{Field,Method} references.
WarningPrinter getMemberNotePrinter = new WarningLogger(logger, configuration.note);
appView.programClassPool.classesAccept(
new AllMethodVisitor(
new AllAttributeVisitor(
new DynamicMemberReferenceInitializer(appView.programClassPool,
appView.libraryClassPool,
getMemberNotePrinter,
createClassMemberNoteExceptionMatcher(configuration.keep, true),
createClassMemberNoteExceptionMatcher(configuration.keep, false)))));
// Initialize other string constant references, if requested.
if (configuration.adaptClassStrings != null)
{
appView.programClassPool.classesAccept(
new ClassNameFilter(configuration.adaptClassStrings,
new AllConstantVisitor(
new StringReferenceInitializer(appView.programClassPool,
appView.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.
appView.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(appView.programClassPool,
appView.libraryClassPool,
null,
null,
null,
dependencyWarningPrinter));
// Reset the library class pool.
appView.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 ClassHierarchyTraveler(true, true, true, false,
new LibraryClassFilter(
new ClassPoolFiller(appView.libraryClassPool))),
new ReferencedClassVisitor(true,
new LibraryClassFilter(
new ClassHierarchyTraveler(true, true, true, false,
new LibraryClassFilter(
new ClassPoolFiller(appView.libraryClassPool)))))
));
}
else
{
// Initialize the class references of all library class members.
appView.libraryClassPool.classesAccept(
new ClassReferenceInitializer(appView.programClassPool,
appView.libraryClassPool,
null,
null,
null,
dependencyWarningPrinter));
}
// Initialize the subclass hierarchies (in the right order,
// with a single instance).
ClassSubHierarchyInitializer classSubHierarchyInitializer =
new ClassSubHierarchyInitializer();
appView.programClassPool.accept(classSubHierarchyInitializer);
appView.libraryClassPool.accept(classSubHierarchyInitializer);
if (configuration.keepKotlinMetadata)
{
appView.resourceFilePool.resourceFilesAccept(new KotlinModuleReferenceInitializer(appView.programClassPool, appView.libraryClassPool));
}
// Share strings between the classes, to reduce heap memory usage.
appView.programClassPool.classesAccept(new StringSharer());
appView.libraryClassPool.classesAccept(new StringSharer());
// Check for any unmatched class members.
WarningPrinter classMemberNotePrinter = new WarningLogger(logger, configuration.note);
if (checkConfiguration)
{
ClassMemberChecker classMemberChecker =
new ClassMemberChecker(appView.programClassPool,
classMemberNotePrinter);
classMemberChecker.checkClassSpecifications(configuration.keep);
classMemberChecker.checkClassSpecifications(configuration.assumeNoSideEffects);
classMemberChecker.checkClassSpecifications(configuration.assumeNoExternalSideEffects);
classMemberChecker.checkClassSpecifications(configuration.assumeNoEscapingParameters);
classMemberChecker.checkClassSpecifications(configuration.assumeNoExternalReturnValues);
}
// Check for unkept descriptor classes of kept class members.
WarningPrinter descriptorKeepNotePrinter = new WarningLogger(logger, configuration.note);
if (checkConfiguration)
{
new DescriptorKeepChecker(appView.programClassPool,
appView.libraryClassPool,
descriptorKeepNotePrinter).checkClassSpecifications(configuration.keep);
}
// Check for keep options that only match library classes.
WarningPrinter libraryKeepNotePrinter = new WarningLogger(logger, configuration.note);
if (checkConfiguration)
{
new LibraryKeepChecker(appView.programClassPool,
appView.libraryClassPool,
libraryKeepNotePrinter).checkClassSpecifications(configuration.keep);
}
// Initialize the references to Java classes in resource files.
appView.resourceFilePool.resourceFilesAccept(
new ResourceJavaReferenceClassInitializer(appView.programClassPool));
// Print out a summary of the notes, if necessary.
int fullyQualifiedNoteCount = fullyQualifiedClassNameNotePrinter.getWarningCount();
if (fullyQualifiedNoteCount > 0)
{
logger.info("Note: there were {} references to unknown classes.", fullyQualifiedNoteCount);
logger.info(" You should check your configuration for typos.");
logger.info(" (https://www.guardsquare.com/proguard/manual/troubleshooting#unknownclass)");
}
int classMemberNoteCount = classMemberNotePrinter.getWarningCount();
if (classMemberNoteCount > 0)
{
logger.info("Note: there were {} references to unknown class members.", classMemberNoteCount);
logger.info(" You should check your configuration for typos.");
}
int getAnnotationNoteCount = getAnnotationNotePrinter.getWarningCount();
if (getAnnotationNoteCount > 0)
{
logger.info("Note: there were {} classes trying to access annotations using reflection.",
getAnnotationNoteCount);
logger.info(" You should consider keeping the annotation attributes");
logger.info(" (using '-keepattributes *Annotation*').");
logger.info(" (https://www.guardsquare.com/proguard/manual/troubleshooting#attributes)");
}
int getSignatureNoteCount = getSignatureNotePrinter.getWarningCount();
if (getSignatureNoteCount > 0)
{
logger.info("Note: there were {} classes trying to access generic signatures using reflection.",
getSignatureNoteCount);
logger.info(" You should consider keeping the signature attributes");
logger.info(" (using '-keepattributes Signature').");
logger.info(" (https://www.guardsquare.com/proguard/manual/troubleshooting#attributes)");
}
int getEnclosingClassNoteCount = getEnclosingClassNotePrinter.getWarningCount();
if (getEnclosingClassNoteCount > 0)
{
logger.info("Note: there were {} classes trying to access enclosing classes using reflection.",
getEnclosingClassNoteCount);
logger.info(" You should consider keeping the inner classes attributes");
logger.info(" (using '-keepattributes InnerClasses').");
logger.info(" (https://www.guardsquare.com/proguard/manual/troubleshooting#attributes)");
}
int getEnclosingMethodNoteCount = getEnclosingMethodNotePrinter.getWarningCount();
if (getEnclosingMethodNoteCount > 0)
{
logger.info("Note: there were {} classes trying to access enclosing methods using reflection.",
getEnclosingMethodNoteCount);
logger.info(" You should consider keeping the enclosing method attributes");
logger.info(" (using '-keepattributes InnerClasses,EnclosingMethod').");
logger.info(" (https://www.guardsquare.com/proguard/manual/troubleshooting#attributes)");
}
int descriptorNoteCount = descriptorKeepNotePrinter.getWarningCount();
if (descriptorNoteCount > 0)
{
logger.info("Note: there were {} unkept descriptor classes in kept class members.",
descriptorNoteCount);
logger.info(" You should consider explicitly keeping the mentioned classes");
logger.info(" (using '-keep').");
logger.info(" (https://www.guardsquare.com/proguard/manual/troubleshooting#descriptorclass)");
}
int libraryNoteCount = libraryKeepNotePrinter.getWarningCount();
if (libraryNoteCount > 0)
{
logger.info("Note: there were {} library classes explicitly being kept.", libraryNoteCount);
logger.info(" You don't need to keep library classes; they are already left unchanged.");
logger.info(" (https://www.guardsquare.com/proguard/manual/troubleshooting#libraryclass)");
}
int dynamicClassReferenceNoteCount = dynamicClassReferenceNotePrinter.getWarningCount();
if (dynamicClassReferenceNoteCount > 0)
{
logger.info("Note: there were {} unresolved dynamic references to classes or interfaces.",
dynamicClassReferenceNoteCount);
logger.info(" You should check if you need to specify additional program jars.");
logger.info(" (https://www.guardsquare.com/proguard/manual/troubleshooting#dynamicalclass)");
}
int classForNameNoteCount = classForNameNotePrinter.getWarningCount();
if (classForNameNoteCount > 0)
{
logger.info("Note: there were {} class casts of dynamically created class instances.",
classForNameNoteCount);
logger.info(" You might consider explicitly keeping the mentioned classes and/or");
logger.info(" their implementations (using '-keep').");
logger.info(" (https://www.guardsquare.com/proguard/manual/troubleshooting#dynamicalclasscast)");
}
int getmemberNoteCount = getMemberNotePrinter.getWarningCount();
if (getmemberNoteCount > 0)
{
logger.info("Note: there were {} accesses to class members by means of reflection.",
getmemberNoteCount);
logger.info(" You should consider explicitly keeping the mentioned class members");
logger.info(" (using '-keep' or '-keepclassmembers').");
logger.info(" (https://www.guardsquare.com/proguard/manual/troubleshooting#dynamicalclassmember)");
}
// Print out a summary of the warnings, if necessary.
int classReferenceWarningCount = classReferenceWarningPrinter.getWarningCount();
if (classReferenceWarningCount > 0)
{
logger.warn("Warning: there were {} unresolved references to classes or interfaces.",
classReferenceWarningCount);
logger.warn(" You may need to add missing library jars or update their versions.");
logger.warn(" If your code works fine without the missing classes, you can suppress");
logger.warn(" the warnings with '-dontwarn' options.");
if (configuration.skipNonPublicLibraryClasses)
{
logger.warn(" You may also have to remove the option '-skipnonpubliclibraryclasses'.");
}
logger.warn(" (https://www.guardsquare.com/proguard/manual/troubleshooting#unresolvedclass)");
}
int dependencyWarningCount = dependencyWarningPrinter.getWarningCount();
if (dependencyWarningCount > 0)
{
logger.warn("Warning: there were {} instances of library classes depending on program classes.",
dependencyWarningCount);
logger.warn(" You must avoid such dependencies, since the program classes will");
logger.warn(" be processed, while the library classes will remain unchanged.");
logger.warn(" (https://www.guardsquare.com/proguard/manual/troubleshooting#dependency)");
}
int programMemberReferenceWarningCount = programMemberReferenceWarningPrinter.getWarningCount();
if (programMemberReferenceWarningCount > 0)
{
logger.warn("Warning: there were {} unresolved references to program class members.",
programMemberReferenceWarningCount);
logger.warn(" Your input classes appear to be inconsistent.");
logger.warn(" You may need to recompile the code.");
logger.warn(" (https://www.guardsquare.com/proguard/manual/troubleshooting#unresolvedprogramclassmember)");
}
int libraryMemberReferenceWarningCount = libraryMemberReferenceWarningPrinter.getWarningCount();
if (libraryMemberReferenceWarningCount > 0)
{
logger.warn("Warning: there were {} unresolved references to library class members.",
libraryMemberReferenceWarningCount);
logger.warn(" You probably need to update the library versions.");
if (!configuration.skipNonPublicLibraryClassMembers)
{
logger.warn(" Alternatively, you may have to specify the option ");
logger.warn(" '-dontskipnonpubliclibraryclassmembers'.");
}
if (configuration.skipNonPublicLibraryClasses)
{
logger.warn(" You may also have to remove the option '-skipnonpubliclibraryclasses'.");
}
logger.warn(" (https://www.guardsquare.com/proguard/manual/troubleshooting#unresolvedlibraryclassmember)");
}
boolean incompatibleOptimization = configuration.optimize && !configuration.shrink;
if (incompatibleOptimization)
{
logger.warn("Warning: optimization is enabled while shrinking is disabled.");
logger.warn(" You need to remove the option -dontshrink or optimization might result in classes that fail verification at runtime.");
}
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))
{
logger.info("Note: you're ignoring all warnings!");
}
logger.info("Ignoring unused library classes...");
logger.info(" Original number of library classes: {}", originalLibraryClassPoolSize);
logger.info(" Final number of library classes: {}", appView.libraryClassPool.size());
if (configuration.keepKotlinMetadata)
{
ClassCounter counter = new ClassCounter();
appView.libraryClassPool.classesAccept(
new ReferencedKotlinMetadataVisitor(
(clazz, kotlinMetadata) -> clazz.accept(counter)
)
);
logger.info(" Number of library classes with @kotlin.Metadata: {}", counter.getCount());
}
}
/**
* Extracts a list of exceptions of classes for which not to print notes,
* from the keep configuration.
*/
private StringMatcher createClassNoteExceptionMatcher(List noteExceptions,
boolean markClasses)
{
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 || !markClasses)
{
// If the class itself is being kept, it's ok.
String className = keepClassSpecification.className;
if (className != null &&
!containsWildCardReferences(className))
{
noteExceptionNames.add(className);
}
// If all of its extensions are being kept, it's ok too.
String extendsClassName = keepClassSpecification.extendsClassName;
if (extendsClassName != null &&
!containsWildCardReferences(extendsClassName))
{
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 &&
!containsWildCardReferences(memberName))
{
noteExceptionNames.add(memberName);
}
}
}
}
if (noteExceptionNames.size() > 0)
{
return new ListParser(new NameParser()).parse(noteExceptionNames);
}
}
return null;
}
/**
* Returns whether the given string contains a numeric reference to a
* wild card ("").
*/
private static boolean containsWildCardReferences(String string)
{
int openIndex = string.indexOf('<');
if (openIndex < 0)
{
return false;
}
int closeIndex = string.indexOf('>', openIndex + 1);
if (closeIndex < 0)
{
return false;
}
try
{
Integer.parseInt(string.substring(openIndex + 1, closeIndex));
}
catch (NumberFormatException e)
{
return false;
}
return true;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy