blob: 3baf4226da6dd0ba4b276cb096af50bab59a8db5 [file] [log] [blame]
/*
* 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.classfile.util;
import proguard.classfile.*;
import proguard.classfile.attribute.*;
import proguard.classfile.attribute.annotation.*;
import proguard.classfile.attribute.annotation.visitor.*;
import proguard.classfile.attribute.visitor.*;
import proguard.classfile.constant.*;
import proguard.classfile.constant.visitor.ConstantVisitor;
import proguard.classfile.visitor.*;
/**
* This ClassVisitor initializes the references of all classes that
* it visits.
* <p>
* All class constant pool entries get direct references to the corresponding
* classes. These references make it more convenient to travel up and across
* the class hierarchy.
* <p>
* All field and method reference constant pool entries get direct references
* to the corresponding classes, fields, and methods.
* <p>
* All name and type constant pool entries get a list of direct references to
* the classes listed in the type.
* <p>
* This visitor optionally prints warnings if some items can't be found.
* <p>
* The class hierarchy must be initialized before using this visitor.
*
* @author Eric Lafortune
*/
public class ClassReferenceInitializer
extends SimplifiedVisitor
implements ClassVisitor,
MemberVisitor,
ConstantVisitor,
AttributeVisitor,
LocalVariableInfoVisitor,
LocalVariableTypeInfoVisitor,
AnnotationVisitor,
ElementValueVisitor
{
private final ClassPool programClassPool;
private final ClassPool libraryClassPool;
private final WarningPrinter missingClassWarningPrinter;
private final WarningPrinter missingProgramMemberWarningPrinter;
private final WarningPrinter missingLibraryMemberWarningPrinter;
private final WarningPrinter dependencyWarningPrinter;
private final MemberFinder memberFinder = new MemberFinder();
/**
* Creates a new ClassReferenceInitializer that initializes the references
* of all visited class files, optionally printing warnings if some classes
* or class members can't be found or if they are in the program class pool.
*/
public ClassReferenceInitializer(ClassPool programClassPool,
ClassPool libraryClassPool,
WarningPrinter missingClassWarningPrinter,
WarningPrinter missingProgramMemberWarningPrinter,
WarningPrinter missingLibraryMemberWarningPrinter,
WarningPrinter dependencyWarningPrinter)
{
this.programClassPool = programClassPool;
this.libraryClassPool = libraryClassPool;
this.missingClassWarningPrinter = missingClassWarningPrinter;
this.missingProgramMemberWarningPrinter = missingProgramMemberWarningPrinter;
this.missingLibraryMemberWarningPrinter = missingLibraryMemberWarningPrinter;
this.dependencyWarningPrinter = dependencyWarningPrinter;
}
// Implementations for ClassVisitor.
public void visitProgramClass(ProgramClass programClass)
{
// Initialize the constant pool entries.
programClass.constantPoolEntriesAccept(this);
// Initialize all fields and methods.
programClass.fieldsAccept(this);
programClass.methodsAccept(this);
// Initialize the attributes.
programClass.attributesAccept(this);
}
public void visitLibraryClass(LibraryClass libraryClass)
{
// Initialize all fields and methods.
libraryClass.fieldsAccept(this);
libraryClass.methodsAccept(this);
}
// Implementations for MemberVisitor.
public void visitProgramField(ProgramClass programClass, ProgramField programField)
{
programField.referencedClass =
findReferencedClass(programClass.getName(),
programField.getDescriptor(programClass));
// Initialize the attributes.
programField.attributesAccept(programClass, this);
}
public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
{
programMethod.referencedClasses =
findReferencedClasses(programClass.getName(),
programMethod.getDescriptor(programClass));
// Initialize the attributes.
programMethod.attributesAccept(programClass, this);
}
public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
{
libraryField.referencedClass =
findReferencedClass(libraryClass.getName(),
libraryField.getDescriptor(libraryClass));
}
public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
{
libraryMethod.referencedClasses =
findReferencedClasses(libraryClass.getName(),
libraryMethod.getDescriptor(libraryClass));
}
// Implementations for ConstantVisitor.
public void visitAnyConstant(Clazz clazz, Constant constant) {}
public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
{
// Fill out the String class.
stringConstant.javaLangStringClass =
findClass(clazz.getName(), ClassConstants.NAME_JAVA_LANG_STRING);
}
public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant)
{
invokeDynamicConstant.referencedClasses =
findReferencedClasses(clazz.getName(),
invokeDynamicConstant.getType(clazz));
}
public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant)
{
// Fill out the MethodHandle class.
methodHandleConstant.javaLangInvokeMethodHandleClass =
findClass(clazz.getName(), ClassConstants.NAME_JAVA_LANG_INVOKE_METHOD_HANDLE);
}
public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
{
String className = refConstant.getClassName(clazz);
// Methods for array types should be found in the Object class.
if (ClassUtil.isInternalArrayType(className))
{
className = ClassConstants.NAME_JAVA_LANG_OBJECT;
}
// See if we can find the referenced class.
// Unresolved references are assumed to refer to library classes
// that will not change anyway.
Clazz referencedClass = findClass(clazz.getName(), className);
if (referencedClass != null)
{
String name = refConstant.getName(clazz);
String type = refConstant.getType(clazz);
boolean isFieldRef = refConstant.getTag() == ClassConstants.CONSTANT_Fieldref;
// See if we can find the referenced class member somewhere in the
// hierarchy.
refConstant.referencedMember = memberFinder.findMember(clazz,
referencedClass,
name,
type,
isFieldRef);
refConstant.referencedClass = memberFinder.correspondingClass();
if (refConstant.referencedMember == null)
{
// We haven't found the class member anywhere in the hierarchy.
boolean isProgramClass = referencedClass instanceof ProgramClass;
WarningPrinter missingMemberWarningPrinter = isProgramClass ?
missingProgramMemberWarningPrinter :
missingLibraryMemberWarningPrinter;
missingMemberWarningPrinter.print(clazz.getName(),
className,
"Warning: " +
ClassUtil.externalClassName(clazz.getName()) +
": can't find referenced " +
(isFieldRef ?
"field '" + ClassUtil.externalFullFieldDescription(0, name, type) :
"method '" + ClassUtil.externalFullMethodDescription(className, 0, name, type)) +
"' in " +
(isProgramClass ?
"program" :
"library") +
" class " +
ClassUtil.externalClassName(className));
}
}
}
public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
{
String className = clazz.getName();
// Fill out the referenced class.
classConstant.referencedClass =
findClass(className, ClassUtil.internalClassNameFromClassType(classConstant.getName(clazz)));
// Fill out the Class class.
classConstant.javaLangClassClass =
findClass(className, ClassConstants.NAME_JAVA_LANG_CLASS);
}
public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant)
{
// Fill out the MethodType class.
methodTypeConstant.javaLangInvokeMethodTypeClass =
findClass(clazz.getName(), ClassConstants.NAME_JAVA_LANG_INVOKE_METHOD_TYPE);
methodTypeConstant.referencedClasses =
findReferencedClasses(clazz.getName(),
methodTypeConstant.getType(clazz));
}
// Implementations for AttributeVisitor.
public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
{
String className = clazz.getName();
String enclosingClassName = enclosingMethodAttribute.getClassName(clazz);
// See if we can find the referenced class.
enclosingMethodAttribute.referencedClass =
findClass(className, enclosingClassName);
if (enclosingMethodAttribute.referencedClass != null)
{
// Is there an enclosing method? Otherwise it's just initialization
// code outside of the constructors.
if (enclosingMethodAttribute.u2nameAndTypeIndex != 0)
{
String name = enclosingMethodAttribute.getName(clazz);
String type = enclosingMethodAttribute.getType(clazz);
// See if we can find the method in the referenced class.
enclosingMethodAttribute.referencedMethod =
enclosingMethodAttribute.referencedClass.findMethod(name, type);
if (enclosingMethodAttribute.referencedMethod == null)
{
// We couldn't find the enclosing method.
missingProgramMemberWarningPrinter.print(className,
enclosingClassName,
"Warning: " +
ClassUtil.externalClassName(className) +
": can't find enclosing method '" +
ClassUtil.externalFullMethodDescription(enclosingClassName, 0, name, type) +
"' in program class " +
ClassUtil.externalClassName(enclosingClassName));
}
}
}
}
public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
{
// Initialize the nested attributes.
codeAttribute.attributesAccept(clazz, method, this);
}
public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
{
// Initialize the local variables.
localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
}
public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
{
// Initialize the local variable types.
localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
}
public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
{
signatureAttribute.referencedClasses =
findReferencedClasses(clazz.getName(),
signatureAttribute.getSignature(clazz));
}
public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
{
// Initialize the annotations.
annotationsAttribute.annotationsAccept(clazz, this);
}
public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
{
// Initialize the annotations.
parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
}
public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
{
// Initialize the annotation.
annotationDefaultAttribute.defaultValueAccept(clazz, this);
}
// Implementations for LocalVariableInfoVisitor.
public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
{
localVariableInfo.referencedClass =
findReferencedClass(clazz.getName(),
localVariableInfo.getDescriptor(clazz));
}
// Implementations for LocalVariableTypeInfoVisitor.
public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
{
localVariableTypeInfo.referencedClasses =
findReferencedClasses(clazz.getName(),
localVariableTypeInfo.getSignature(clazz));
}
// Implementations for AnnotationVisitor.
public void visitAnnotation(Clazz clazz, Annotation annotation)
{
annotation.referencedClasses =
findReferencedClasses(clazz.getName(),
annotation.getType(clazz));
// Initialize the element values.
annotation.elementValuesAccept(clazz, this);
}
// Implementations for ElementValueVisitor.
public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
{
initializeElementValue(clazz, annotation, constantElementValue);
}
public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
{
initializeElementValue(clazz, annotation, enumConstantElementValue);
enumConstantElementValue.referencedClasses =
findReferencedClasses(clazz.getName(),
enumConstantElementValue.getTypeName(clazz));
}
public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
{
initializeElementValue(clazz, annotation, classElementValue);
classElementValue.referencedClasses =
findReferencedClasses(clazz.getName(),
classElementValue.getClassName(clazz));
}
public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
{
initializeElementValue(clazz, annotation, annotationElementValue);
// Initialize the annotation.
annotationElementValue.annotationAccept(clazz, this);
}
public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
{
initializeElementValue(clazz, annotation, arrayElementValue);
// Initialize the element values.
arrayElementValue.elementValuesAccept(clazz, annotation, this);
}
/**
* Initializes the referenced method of an element value, if any.
*/
private void initializeElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue)
{
// See if we have a referenced class.
if (annotation != null &&
annotation.referencedClasses != null &&
elementValue.u2elementNameIndex != 0)
{
// See if we can find the method in the referenced class
// (ignoring the descriptor).
String name = elementValue.getMethodName(clazz);
Clazz referencedClass = annotation.referencedClasses[0];
elementValue.referencedClass = referencedClass;
elementValue.referencedMethod = referencedClass.findMethod(name, null);
}
}
// Small utility methods.
/**
* Returns the single class referenced by the given descriptor, or
* <code>null</code> if there isn't any useful reference.
*/
private Clazz findReferencedClass(String referencingClassName,
String descriptor)
{
DescriptorClassEnumeration enumeration =
new DescriptorClassEnumeration(descriptor);
enumeration.nextFluff();
if (enumeration.hasMoreClassNames())
{
return findClass(referencingClassName, enumeration.nextClassName());
}
return null;
}
/**
* Returns an array of classes referenced by the given descriptor, or
* <code>null</code> if there aren't any useful references.
*/
private Clazz[] findReferencedClasses(String referencingClassName,
String descriptor)
{
DescriptorClassEnumeration enumeration =
new DescriptorClassEnumeration(descriptor);
int classCount = enumeration.classCount();
if (classCount > 0)
{
Clazz[] referencedClasses = new Clazz[classCount];
boolean foundReferencedClasses = false;
for (int index = 0; index < classCount; index++)
{
String fluff = enumeration.nextFluff();
String name = enumeration.nextClassName();
Clazz referencedClass = findClass(referencingClassName, name);
if (referencedClass != null)
{
referencedClasses[index] = referencedClass;
foundReferencedClasses = true;
}
}
if (foundReferencedClasses)
{
return referencedClasses;
}
}
return null;
}
/**
* Returns the class with the given name, either for the program class pool
* or from the library class pool, or <code>null</code> if it can't be found.
*/
private Clazz findClass(String referencingClassName, String name)
{
// Is it an array type?
if (ClassUtil.isInternalArrayType(name))
{
// Ignore any primitive array types.
if (!ClassUtil.isInternalClassType(name))
{
return null;
}
// Strip the array part.
name = ClassUtil.internalClassNameFromClassType(name);
}
// First look for the class in the program class pool.
Clazz clazz = programClassPool.getClass(name);
// Otherwise look for the class in the library class pool.
if (clazz == null)
{
clazz = libraryClassPool.getClass(name);
if (clazz == null &&
missingClassWarningPrinter != null)
{
// We didn't find the superclass or interface. Print a warning.
missingClassWarningPrinter.print(referencingClassName,
name,
"Warning: " +
ClassUtil.externalClassName(referencingClassName) +
": can't find referenced class " +
ClassUtil.externalClassName(name));
}
}
else if (dependencyWarningPrinter != null)
{
// The superclass or interface was found in the program class pool.
// Print a warning.
dependencyWarningPrinter.print(referencingClassName,
name,
"Warning: library class " +
ClassUtil.externalClassName(referencingClassName) +
" depends on program class " +
ClassUtil.externalClassName(name));
}
return clazz;
}
}