diff options
Diffstat (limited to 'src/proguard/classfile/util')
18 files changed, 5866 insertions, 0 deletions
diff --git a/src/proguard/classfile/util/AccessUtil.java b/src/proguard/classfile/util/AccessUtil.java new file mode 100644 index 0000000..3ad6961 --- /dev/null +++ b/src/proguard/classfile/util/AccessUtil.java @@ -0,0 +1,105 @@ +/* + * 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.classfile.util; + +import proguard.classfile.ClassConstants; + + +/** + * Utility methods for working with access flags. For convenience, this class + * defines access levels, in ascending order: <code>PRIVATE</code>, + * <code>PACKAGE_VISIBLE</code>, <code>PROTECTED</code>, and <code>PUBLIC</code>. + * + * @author Eric Lafortune + */ +public class AccessUtil +{ + public static final int PRIVATE = 0; + public static final int PACKAGE_VISIBLE = 1; + public static final int PROTECTED = 2; + public static final int PUBLIC = 3; + + + // The mask of access flags. + private static final int ACCESS_MASK = + ClassConstants.INTERNAL_ACC_PUBLIC | + ClassConstants.INTERNAL_ACC_PRIVATE | + ClassConstants.INTERNAL_ACC_PROTECTED; + + + /** + * Returns the corresponding access level of the given access flags. + * @param accessFlags the internal access flags. + * @return the corresponding access level: <code>PRIVATE</code>, + * <code>PACKAGE_VISIBLE</code>, <code>PROTECTED</code>, or + * <code>PUBLIC</code>. + */ + public static int accessLevel(int accessFlags) + { + switch (accessFlags & ACCESS_MASK) + { + case ClassConstants.INTERNAL_ACC_PRIVATE: return PRIVATE; + default: return PACKAGE_VISIBLE; + case ClassConstants.INTERNAL_ACC_PROTECTED: return PROTECTED; + case ClassConstants.INTERNAL_ACC_PUBLIC: return PUBLIC; + } + } + + + /** + * Returns the corresponding access flags of the given access level. + * @param accessLevel the access level: <code>PRIVATE</code>, + * <code>PACKAGE_VISIBLE</code>, <code>PROTECTED</code>, + * or <code>PUBLIC</code>. + * @return the corresponding internal access flags, the internal access + * flags as a logical bit mask of <code>INTERNAL_ACC_PRIVATE</code>, + * <code>INTERNAL_ACC_PROTECTED</code>, and + * <code>INTERNAL_ACC_PUBLIC</code>. + */ + public static int accessFlags(int accessLevel) + { + switch (accessLevel) + { + case PRIVATE: return ClassConstants.INTERNAL_ACC_PRIVATE; + default: return 0; + case PROTECTED: return ClassConstants.INTERNAL_ACC_PROTECTED; + case PUBLIC: return ClassConstants.INTERNAL_ACC_PUBLIC; + } + } + + + /** + * Replaces the access part of the given access flags. + * @param accessFlags the internal access flags. + * @param accessFlags the new internal access flags. + */ + public static int replaceAccessFlags(int accessFlags, int newAccessFlags) + { + // A private class member should not be explicitly final. + if (newAccessFlags == ClassConstants.INTERNAL_ACC_PRIVATE) + { + accessFlags &= ~ClassConstants.INTERNAL_ACC_FINAL; + } + + return (accessFlags & ~ACCESS_MASK) | + (newAccessFlags & ACCESS_MASK); + } +} diff --git a/src/proguard/classfile/util/ClassReferenceInitializer.java b/src/proguard/classfile/util/ClassReferenceInitializer.java new file mode 100644 index 0000000..b1f2c27 --- /dev/null +++ b/src/proguard/classfile/util/ClassReferenceInitializer.java @@ -0,0 +1,545 @@ +/* + * 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.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 missingMemberWarningPrinter; + 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 missingMemberWarningPrinter, + WarningPrinter dependencyWarningPrinter) + { + this.programClassPool = programClassPool; + this.libraryClassPool = libraryClassPool; + this.missingClassWarningPrinter = missingClassWarningPrinter; + this.missingMemberWarningPrinter = missingMemberWarningPrinter; + 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.INTERNAL_NAME_JAVA_LANG_STRING); + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + String className = refConstant.getClassName(clazz); + + // 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 && + !ClassUtil.isInternalArrayType(className)) + { + 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've haven't found the class member anywhere in the hierarchy. + 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 class " + + ClassUtil.externalClassName(className)); + } + } + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + String className = clazz.getName(); + + // Fill out the referenced class. + classConstant.referencedClass = + findClass(className, classConstant.getName(clazz)); + + // Fill out the Class class. + classConstant.javaLangClassClass = + findClass(className, ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS); + } + + + // 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. + Clazz referencedClass = findClass(className, enclosingClassName); + + if (referencedClass == null) + { + // We couldn't find the enclosing class. + missingClassWarningPrinter.print(className, + enclosingClassName, + "Warning: " + + ClassUtil.externalClassName(className) + + ": can't find enclosing class " + + ClassUtil.externalClassName(enclosingClassName)); + return; + } + + // Make sure there is actually an enclosed method. + if (enclosingMethodAttribute.u2nameAndTypeIndex == 0) + { + return; + } + + String name = enclosingMethodAttribute.getName(clazz); + String type = enclosingMethodAttribute.getType(clazz); + + // See if we can find the method in the referenced class. + Method referencedMethod = referencedClass.findMethod(name, type); + + if (referencedMethod == null) + { + // We couldn't find the enclosing method. + missingMemberWarningPrinter.print(className, + enclosingClassName, + "Warning: " + + ClassUtil.externalClassName(className) + + ": can't find enclosing method '" + + ClassUtil.externalFullMethodDescription(enclosingClassName, 0, name, type) + + "' in class " + + ClassUtil.externalClassName(enclosingClassName)); + return; + } + + // Save the references. + enclosingMethodAttribute.referencedClass = referencedClass; + enclosingMethodAttribute.referencedMethod = referencedMethod; + } + + + 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(), + clazz.getString(signatureAttribute.u2signatureIndex)); + } + + + 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(), + clazz.getString(localVariableInfo.u2descriptorIndex)); + } + + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + localVariableTypeInfo.referencedClasses = + findReferencedClasses(clazz.getName(), + clazz.getString(localVariableTypeInfo.u2signatureIndex)); + } + + + // Implementations for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + annotation.referencedClasses = + findReferencedClasses(clazz.getName(), + clazz.getString(annotation.u2typeIndex)); + + // 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(), + clazz.getString(enumConstantElementValue.u2typeNameIndex)); + } + + + public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) + { + initializeElementValue(clazz, annotation, classElementValue); + + classElementValue.referencedClasses = + findReferencedClasses(clazz.getName(), + clazz.getString(classElementValue.u2classInfoIndex)); + } + + + 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 = clazz.getString(elementValue.u2elementNameIndex); + + 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) + { + // Ignore any primitive array types. + if (ClassUtil.isInternalArrayType(name) && + !ClassUtil.isInternalClassType(name)) + { + return null; + } + + // 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; + } +} diff --git a/src/proguard/classfile/util/ClassSubHierarchyInitializer.java b/src/proguard/classfile/util/ClassSubHierarchyInitializer.java new file mode 100644 index 0000000..30fd526 --- /dev/null +++ b/src/proguard/classfile/util/ClassSubHierarchyInitializer.java @@ -0,0 +1,77 @@ +/* + * 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.classfile.util; + +import proguard.classfile.*; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This ClassVisitor adds all classes that it visits to the list of subclasses + * of their superclass. These subclass lists make it more convenient to travel + * + * @author Eric Lafortune + */ +public class ClassSubHierarchyInitializer +implements ClassVisitor +{ + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Add this class to the subclasses of its superclass. + addSubclass(programClass, programClass.getSuperClass()); + + // Add this class to the subclasses of its interfaces. + for (int index = 0; index < programClass.u2interfacesCount; index++) + { + addSubclass(programClass, programClass.getInterface(index)); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + // Add this class to the subclasses of its superclass, + addSubclass(libraryClass, libraryClass.superClass); + + // Add this class to the subclasses of its interfaces. + Clazz[] interfaceClasses = libraryClass.interfaceClasses; + if (interfaceClasses != null) + { + for (int index = 0; index < interfaceClasses.length; index++) + { + // Add this class to the subclasses of the interface class. + addSubclass(libraryClass, interfaceClasses[index]); + } + } + } + + + // Small utility methods. + + private void addSubclass(Clazz subclass, Clazz clazz) + { + if (clazz != null) + { + clazz.addSubClass(subclass); + } + } +} diff --git a/src/proguard/classfile/util/ClassSuperHierarchyInitializer.java b/src/proguard/classfile/util/ClassSuperHierarchyInitializer.java new file mode 100644 index 0000000..af2a209 --- /dev/null +++ b/src/proguard/classfile/util/ClassSuperHierarchyInitializer.java @@ -0,0 +1,168 @@ +/* + * 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.classfile.util; + +import proguard.classfile.*; +import proguard.classfile.constant.ClassConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This ClassVisitor initializes the superclass hierarchy of all classes that + * it visits. + * <p> + * Visited library classes get direct references to their superclasses and + * interfaces, replacing the superclass names and interface names. The direct + * references are equivalent to the names, but they are more efficient to work + * with. + * <p> + * This visitor optionally prints warnings if some superclasses can't be found + * or if they are in the program class pool. + * + * @author Eric Lafortune + */ +public class ClassSuperHierarchyInitializer +extends SimplifiedVisitor +implements ClassVisitor, + ConstantVisitor +{ + private final ClassPool programClassPool; + private final ClassPool libraryClassPool; + private final WarningPrinter missingWarningPrinter; + private final WarningPrinter dependencyWarningPrinter; + + + /** + * Creates a new ClassSuperHierarchyInitializer that initializes the super + * hierarchy of all visited class files, optionally printing warnings if + * some classes can't be found or if they are in the program class pool. + */ + public ClassSuperHierarchyInitializer(ClassPool programClassPool, + ClassPool libraryClassPool, + WarningPrinter missingWarningPrinter, + WarningPrinter dependencyWarningPrinter) + { + this.programClassPool = programClassPool; + this.libraryClassPool = libraryClassPool; + this.missingWarningPrinter = missingWarningPrinter; + this.dependencyWarningPrinter = dependencyWarningPrinter; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Link to the super class. + programClass.superClassConstantAccept(this); + + // Link to the interfaces. + programClass.interfaceConstantsAccept(this); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + String className = libraryClass.getName(); + + // Link to the super class. + String superClassName = libraryClass.superClassName; + if (superClassName != null) + { + // Keep a reference to the superclass. + libraryClass.superClass = findClass(className, superClassName); + } + + // Link to the interfaces. + if (libraryClass.interfaceNames != null) + { + String[] interfaceNames = libraryClass.interfaceNames; + Clazz[] interfaceClasses = new Clazz[interfaceNames.length]; + + for (int index = 0; index < interfaceNames.length; index++) + { + // Keep a reference to the interface class. + interfaceClasses[index] = + findClass(className, interfaceNames[index]); + } + + libraryClass.interfaceClasses = interfaceClasses; + } + + // Discard the name Strings. From now on, we'll use the object + // references. + libraryClass.superClassName = null; + libraryClass.interfaceNames = null; + } + + + // Implementations for ConstantVisitor. + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + classConstant.referencedClass = + findClass(clazz.getName(), classConstant.getName(clazz)); + } + + + // Small utility methods. + + /** + * 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) + { + // 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 && + missingWarningPrinter != null) + { + // We didn't find the superclass or interface. Print a warning. + missingWarningPrinter.print(referencingClassName, + name, + "Warning: " + + ClassUtil.externalClassName(referencingClassName) + + ": can't find superclass or interface " + + 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) + + " extends or implements program class " + + ClassUtil.externalClassName(name)); + } + + return clazz; + } +} diff --git a/src/proguard/classfile/util/ClassUtil.java b/src/proguard/classfile/util/ClassUtil.java new file mode 100644 index 0000000..5f25f01 --- /dev/null +++ b/src/proguard/classfile/util/ClassUtil.java @@ -0,0 +1,1154 @@ +/* + * 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.classfile.util; + +import proguard.classfile.ClassConstants; + +import java.util.List; + +/** + * Utility methods for converting between internal and external representations + * of names and descriptions. + * + * @author Eric Lafortune + */ +public class ClassUtil +{ + private static final String EMPTY_STRING = ""; + + + /** + * Checks whether the given class magic number is correct. + * @param magicNumber the magic number. + * @throws UnsupportedOperationException when the magic number is incorrect. + */ + public static void checkMagicNumber(int magicNumber) throws UnsupportedOperationException + { + if (magicNumber != ClassConstants.MAGIC) + { + throw new UnsupportedOperationException("Invalid magic number ["+Integer.toHexString(magicNumber)+"] in class"); + } + } + + + /** + * Returns the combined class version number. + * @param majorVersion the major part of the class version number. + * @param minorVersion the minor part of the class version number. + * @return the combined class version number. + */ + public static int internalClassVersion(int majorVersion, int minorVersion) + { + return (majorVersion << 16) | minorVersion; + } + + + /** + * Returns the major part of the given class version number. + * @param classVersion the combined class version number. + * @return the major part of the class version number. + */ + public static int internalMajorClassVersion(int classVersion) + { + return classVersion >>> 16; + } + + + /** + * Returns the internal class version number. + * @param classVersion the external class version number. + * @return the internal class version number. + */ + public static int internalMinorClassVersion(int classVersion) + { + return classVersion & 0xffff; + } + + + /** + * Returns the internal class version number. + * @param classVersion the external class version number. + * @return the internal class version number. + */ + public static int internalClassVersion(String classVersion) + { + return + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_0) || + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_1) ? ClassConstants.INTERNAL_CLASS_VERSION_1_0 : + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_2) ? ClassConstants.INTERNAL_CLASS_VERSION_1_2 : + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_3) ? ClassConstants.INTERNAL_CLASS_VERSION_1_3 : + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_4) ? ClassConstants.INTERNAL_CLASS_VERSION_1_4 : + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_5_ALIAS) || + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_5) ? ClassConstants.INTERNAL_CLASS_VERSION_1_5 : + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_6_ALIAS) || + classVersion.equals(ClassConstants.EXTERNAL_CLASS_VERSION_1_6) ? ClassConstants.INTERNAL_CLASS_VERSION_1_6 : + 0; + } + + + /** + * Returns the minor part of the given class version number. + * @param classVersion the combined class version number. + * @return the minor part of the class version number. + */ + public static String externalClassVersion(int classVersion) + { + switch (classVersion) + { + case ClassConstants.INTERNAL_CLASS_VERSION_1_0: return ClassConstants.EXTERNAL_CLASS_VERSION_1_0; + case ClassConstants.INTERNAL_CLASS_VERSION_1_2: return ClassConstants.EXTERNAL_CLASS_VERSION_1_2; + case ClassConstants.INTERNAL_CLASS_VERSION_1_3: return ClassConstants.EXTERNAL_CLASS_VERSION_1_3; + case ClassConstants.INTERNAL_CLASS_VERSION_1_4: return ClassConstants.EXTERNAL_CLASS_VERSION_1_4; + case ClassConstants.INTERNAL_CLASS_VERSION_1_5: return ClassConstants.EXTERNAL_CLASS_VERSION_1_5; + case ClassConstants.INTERNAL_CLASS_VERSION_1_6: return ClassConstants.EXTERNAL_CLASS_VERSION_1_6; + default: return null; + } + } + + + /** + * Checks whether the given class version number is supported. + * @param classVersion the combined class version number. + * @throws UnsupportedOperationException when the version is not supported. + */ + public static void checkVersionNumbers(int classVersion) throws UnsupportedOperationException + { + if (classVersion < ClassConstants.INTERNAL_CLASS_VERSION_1_0 || + classVersion > ClassConstants.INTERNAL_CLASS_VERSION_1_6) + { + throw new UnsupportedOperationException("Unsupported version number ["+ + internalMajorClassVersion(classVersion)+"."+ + internalMinorClassVersion(classVersion)+"] for class format"); + } + } + + + /** + * Converts an external class name into an internal class name. + * @param externalClassName the external class name, + * e.g. "<code>java.lang.Object</code>" + * @return the internal class name, + * e.g. "<code>java/lang/Object</code>". + */ + public static String internalClassName(String externalClassName) + { + return externalClassName.replace(ClassConstants.EXTERNAL_PACKAGE_SEPARATOR, + ClassConstants.INTERNAL_PACKAGE_SEPARATOR); + } + + + /** + * Converts an internal class description into an external class description. + * @param accessFlags the access flags of the class. + * @param internalClassName the internal class name, + * e.g. "<code>java/lang/Object</code>". + * @return the external class description, + * e.g. "<code>public java.lang.Object</code>". + */ + public static String externalFullClassDescription(int accessFlags, + String internalClassName) + { + return externalClassAccessFlags(accessFlags) + + externalClassName(internalClassName); + } + + + /** + * Converts an internal class name into an external class name. + * @param internalClassName the internal class name, + * e.g. "<code>java/lang/Object</code>". + * @return the external class name, + * e.g. "<code>java.lang.Object</code>". + */ + public static String externalClassName(String internalClassName) + { + return //internalClassName.startsWith(ClassConstants.INTERNAL_PACKAGE_JAVA_LANG) && + //internalClassName.indexOf(ClassConstants.INTERNAL_PACKAGE_SEPARATOR, ClassConstants.INTERNAL_PACKAGE_JAVA_LANG.length() + 1) < 0 ? + //internalClassName.substring(ClassConstants.INTERNAL_PACKAGE_JAVA_LANG.length()) : + internalClassName.replace(ClassConstants.INTERNAL_PACKAGE_SEPARATOR, + ClassConstants.EXTERNAL_PACKAGE_SEPARATOR); + } + + + /** + * Converts an internal class name into an external short class name, without + * package specification. + * @param externalClassName the external class name, + * e.g. "<code>java.lang.Object</code>" + * @return the external short class name, + * e.g. "<code>Object</code>". + */ + public static String externalShortClassName(String externalClassName) + { + int index = externalClassName.lastIndexOf(ClassConstants.EXTERNAL_PACKAGE_SEPARATOR); + return externalClassName.substring(index+1); + } + + + /** + * Returns whether the given internal type is an array type. + * @param internalType the internal type, + * e.g. "<code>[[Ljava/lang/Object;</code>". + * @return <code>true</code> if the given type is an array type, + * <code>false</code> otherwise. + */ + public static boolean isInternalArrayType(String internalType) + { + return internalType.length() > 1 && + internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_ARRAY; + } + + + /** + * Returns the number of dimensions of the given internal type. + * @param internalType the internal type, + * e.g. "<code>[[Ljava/lang/Object;</code>". + * @return the number of dimensions, e.g. 2. + */ + public static int internalArrayTypeDimensionCount(String internalType) + { + int dimensions = 0; + while (internalType.charAt(dimensions) == ClassConstants.INTERNAL_TYPE_ARRAY) + { + dimensions++; + } + + return dimensions; + } + + + /** + * Returns whether the given internal class name is one of the interfaces + * that is implemented by all array types. These class names are + * "<code>java/lang/Object</code>", "<code>java/lang/Cloneable</code>", and + * "<code>java/io/Serializable</code>" + * @param internalClassName the internal class name, + * e.g. "<code>java/lang/Object</code>". + * @return <code>true</code> if the given type is an array interface name, + * <code>false</code> otherwise. + */ + public static boolean isInternalArrayInterfaceName(String internalClassName) + { + return ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT.equals(internalClassName) || + ClassConstants.INTERNAL_NAME_JAVA_LANG_CLONEABLE.equals(internalClassName) || + ClassConstants.INTERNAL_NAME_JAVA_IO_SERIALIZABLE.equals(internalClassName); + } + + + /** + * Returns whether the given internal type is a plain primitive type + * (not void). + * @param internalType the internal type, + * e.g. "<code>I</code>". + * @return <code>true</code> if the given type is a class type, + * <code>false</code> otherwise. + */ + public static boolean isInternalPrimitiveType(char internalType) + { + return internalType == ClassConstants.INTERNAL_TYPE_BOOLEAN || + internalType == ClassConstants.INTERNAL_TYPE_BYTE || + internalType == ClassConstants.INTERNAL_TYPE_CHAR || + internalType == ClassConstants.INTERNAL_TYPE_SHORT || + internalType == ClassConstants.INTERNAL_TYPE_INT || + internalType == ClassConstants.INTERNAL_TYPE_FLOAT || + internalType == ClassConstants.INTERNAL_TYPE_LONG || + internalType == ClassConstants.INTERNAL_TYPE_DOUBLE; + } + + + /** + * Returns whether the given internal type is a primitive Category 2 type. + * @param internalType the internal type, + * e.g. "<code>L</code>". + * @return <code>true</code> if the given type is a Category 2 type, + * <code>false</code> otherwise. + */ + public static boolean isInternalCategory2Type(String internalType) + { + return internalType.length() == 1 && + (internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_LONG || + internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_DOUBLE); + } + + + /** + * Returns whether the given internal type is a plain class type + * (including an array type of a plain class type). + * @param internalType the internal type, + * e.g. "<code>Ljava/lang/Object;</code>". + * @return <code>true</code> if the given type is a class type, + * <code>false</code> otherwise. + */ + public static boolean isInternalClassType(String internalType) + { + int length = internalType.length(); + return length > 1 && +// internalType.charAt(0) == ClassConstants.INTERNAL_TYPE_CLASS_START && + internalType.charAt(length-1) == ClassConstants.INTERNAL_TYPE_CLASS_END; + } + + + /** + * Returns the internal type of a given class name. + * @param internalClassName the internal class name, + * e.g. "<code>java/lang/Object</code>". + * @return the internal type, + * e.g. "<code>Ljava/lang/Object;</code>". + */ + public static String internalTypeFromClassName(String internalClassName) + { + return internalArrayTypeFromClassName(internalClassName, 0); + } + + + /** + * Returns the internal array type of a given class name with a given number + * of dimensions. If the number of dimensions is 0, the class name itself is + * returned. + * @param internalClassName the internal class name, + * e.g. "<code>java/lang/Object</code>". + * @param dimensionCount the number of array dimensions. + * @return the internal array type of the array elements, + * e.g. "<code>Ljava/lang/Object;</code>". + */ + public static String internalArrayTypeFromClassName(String internalClassName, + int dimensionCount) + { + StringBuffer buffer = new StringBuffer(internalClassName.length() + dimensionCount + 2); + + for (int dimension = 0; dimension < dimensionCount; dimension++) + { + buffer.append(ClassConstants.INTERNAL_TYPE_ARRAY); + } + + return buffer.append(ClassConstants.INTERNAL_TYPE_CLASS_START) + .append(internalClassName) + .append(ClassConstants.INTERNAL_TYPE_CLASS_END) + .toString(); + } + + + /** + * Returns the internal element type of a given internal array type. + * @param internalArrayType the internal array type, + * e.g. "<code>[[Ljava/lang/Object;</code>" or + * "<code>[I</code>". + * @return the internal type of the array elements, + * e.g. "<code>Ljava/lang/Object;</code>" or + * "<code>I</code>". + */ + public static String internalTypeFromArrayType(String internalArrayType) + { + int index = internalArrayType.lastIndexOf(ClassConstants.INTERNAL_TYPE_ARRAY); + return internalArrayType.substring(index+1); + } + + + /** + * Returns the internal class name of a given internal class type + * (including an array type). Types involving primitive types are returned + * unchanged. + * @param internalClassType the internal class type, + * e.g. "<code>[Ljava/lang/Object;</code>", + * "<code>Ljava/lang/Object;</code>", or + * "<code>java/lang/Object</code>". + * @return the internal class name, + * e.g. "<code>java/lang/Object</code>". + */ + public static String internalClassNameFromClassType(String internalClassType) + { + return isInternalClassType(internalClassType) ? + internalClassType.substring(internalClassType.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_START)+1, + internalClassType.length()-1) : + internalClassType; + } + + + /** + * Returns the internal class name of any given internal descriptor type, + * disregarding array prefixes. + * @param internalClassType the internal class type, + * e.g. "<code>Ljava/lang/Object;</code>" or + * "<code>[[I</code>". + * @return the internal class name, + * e.g. "<code>java/lang/Object</code>" or + * <code>null</code>. + */ + public static String internalClassNameFromType(String internalClassType) + { + if (!isInternalClassType(internalClassType)) + { + return null; + } + + // Is it an array type? + if (isInternalArrayType(internalClassType)) + { + internalClassType = internalTypeFromArrayType(internalClassType); + } + + return internalClassNameFromClassType(internalClassType); + } + + + /** + * Returns the internal type of the given internal method descriptor. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(II)Z</code>". + * @return the internal return type, + * e.g. "<code>Z</code>". + */ + public static String internalMethodReturnType(String internalMethodDescriptor) + { + int index = internalMethodDescriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); + return internalMethodDescriptor.substring(index + 1); + } + + + /** + * Returns the number of parameters of the given internal method descriptor. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(ID)Z</code>". + * @return the number of parameters, + * e.g. 2. + */ + public static int internalMethodParameterCount(String internalMethodDescriptor) + { + InternalTypeEnumeration internalTypeEnumeration = + new InternalTypeEnumeration(internalMethodDescriptor); + + int counter = 0; + while (internalTypeEnumeration.hasMoreTypes()) + { + internalTypeEnumeration.nextType(); + + counter++; + } + + return counter; + } + + + /** + * Returns the size taken up on the stack by the parameters of the given + * internal method descriptor. This accounts for long and double parameters + * taking up two entries. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(ID)Z</code>". + * @return the size taken up on the stack, + * e.g. 3. + */ + public static int internalMethodParameterSize(String internalMethodDescriptor) + { + return internalMethodParameterSize(internalMethodDescriptor, true); + } + + + /** + * Returns the size taken up on the stack by the parameters of the given + * internal method descriptor. This accounts for long and double parameters + * taking up two entries, and a non-static method taking up an additional + * entry. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(ID)Z</code>". + * @param accessFlags the access flags of the method, + * e.g. 0. + * @return the size taken up on the stack, + * e.g. 4. + */ + public static int internalMethodParameterSize(String internalMethodDescriptor, + int accessFlags) + { + return internalMethodParameterSize(internalMethodDescriptor, + (accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0); + } + + + /** + * Returns the size taken up on the stack by the parameters of the given + * internal method descriptor. This accounts for long and double parameters + * taking up two spaces, and a non-static method taking up an additional + * entry. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(ID)Z</code>". + * @param isStatic specifies whether the method is static, + * e.g. false. + * @return the size taken up on the stack, + * e.g. 4. + */ + public static int internalMethodParameterSize(String internalMethodDescriptor, + boolean isStatic) + { + InternalTypeEnumeration internalTypeEnumeration = + new InternalTypeEnumeration(internalMethodDescriptor); + + int size = isStatic ? 0 : 1; + while (internalTypeEnumeration.hasMoreTypes()) + { + String internalType = internalTypeEnumeration.nextType(); + + size += internalTypeSize(internalType); + } + + return size; + } + + + /** + * Returns the size taken up on the stack by the given internal type. + * The size is 1, except for long and double types, for which it is 2, + * and for the void type, for which 0 is returned. + * @param internalType the internal type, + * e.g. "<code>I</code>". + * @return the size taken up on the stack, + * e.g. 1. + */ + public static int internalTypeSize(String internalType) + { + if (internalType.length() == 1) + { + char internalPrimitiveType = internalType.charAt(0); + if (internalPrimitiveType == ClassConstants.INTERNAL_TYPE_LONG || + internalPrimitiveType == ClassConstants.INTERNAL_TYPE_DOUBLE) + { + return 2; + } + else if (internalPrimitiveType == ClassConstants.INTERNAL_TYPE_VOID) + { + return 0; + } + } + + return 1; + } + + + /** + * Converts an external type into an internal type. + * @param externalType the external type, + * e.g. "<code>java.lang.Object[][]</code>" or + * "<code>int[]</code>". + * @return the internal type, + * e.g. "<code>[[Ljava/lang/Object;</code>" or + * "<code>[I</code>". + */ + public static String internalType(String externalType) + { + // Strip the array part, if any. + int dimensionCount = externalArrayTypeDimensionCount(externalType); + if (dimensionCount > 0) + { + externalType = externalType.substring(0, externalType.length() - dimensionCount * ClassConstants.EXTERNAL_TYPE_ARRAY.length()); + } + + // Analyze the actual type part. + char internalTypeChar = + externalType.equals(ClassConstants.EXTERNAL_TYPE_VOID ) ? + ClassConstants.INTERNAL_TYPE_VOID : + externalType.equals(ClassConstants.EXTERNAL_TYPE_BOOLEAN) ? + ClassConstants.INTERNAL_TYPE_BOOLEAN : + externalType.equals(ClassConstants.EXTERNAL_TYPE_BYTE ) ? + ClassConstants.INTERNAL_TYPE_BYTE : + externalType.equals(ClassConstants.EXTERNAL_TYPE_CHAR ) ? + ClassConstants.INTERNAL_TYPE_CHAR : + externalType.equals(ClassConstants.EXTERNAL_TYPE_SHORT ) ? + ClassConstants.INTERNAL_TYPE_SHORT : + externalType.equals(ClassConstants.EXTERNAL_TYPE_INT ) ? + ClassConstants.INTERNAL_TYPE_INT : + externalType.equals(ClassConstants.EXTERNAL_TYPE_FLOAT ) ? + ClassConstants.INTERNAL_TYPE_FLOAT : + externalType.equals(ClassConstants.EXTERNAL_TYPE_LONG ) ? + ClassConstants.INTERNAL_TYPE_LONG : + externalType.equals(ClassConstants.EXTERNAL_TYPE_DOUBLE ) ? + ClassConstants.INTERNAL_TYPE_DOUBLE : + externalType.equals("%" ) ? + '%' : + (char)0; + + String internalType = + internalTypeChar != 0 ? String.valueOf(internalTypeChar) : + ClassConstants.INTERNAL_TYPE_CLASS_START + + internalClassName(externalType) + + ClassConstants.INTERNAL_TYPE_CLASS_END; + + // Prepend the array part, if any. + for (int count = 0; count < dimensionCount; count++) + { + internalType = ClassConstants.INTERNAL_TYPE_ARRAY + internalType; + } + + return internalType; + } + + + /** + * Returns the number of dimensions of the given external type. + * @param externalType the external type, + * e.g. "<code>[[Ljava/lang/Object;</code>". + * @return the number of dimensions, e.g. 2. + */ + public static int externalArrayTypeDimensionCount(String externalType) + { + int dimensions = 0; + int length = ClassConstants.EXTERNAL_TYPE_ARRAY.length(); + int offset = externalType.length() - length; + while (externalType.regionMatches(offset, + ClassConstants.EXTERNAL_TYPE_ARRAY, + 0, + length)) + { + dimensions++; + offset -= length; + } + + return dimensions; + } + + + /** + * Converts an internal type into an external type. + * @param internalType the internal type, + * e.g. "<code>[[Ljava/lang/Object;</code>" or + * "<code>[I</code>". + * @return the external type, + * e.g. "<code>java.lang.Object[][]</code>" or + * "<code>int[]</code>". + */ + public static String externalType(String internalType) + { + // Strip the array part, if any. + int dimensionCount = internalArrayTypeDimensionCount(internalType); + if (dimensionCount > 0) + { + internalType = internalType.substring(dimensionCount); + } + + // Analyze the actual type part. + char internalTypeChar = internalType.charAt(0); + + String externalType = + internalTypeChar == ClassConstants.INTERNAL_TYPE_VOID ? + ClassConstants.EXTERNAL_TYPE_VOID : + internalTypeChar == ClassConstants.INTERNAL_TYPE_BOOLEAN ? + ClassConstants.EXTERNAL_TYPE_BOOLEAN : + internalTypeChar == ClassConstants.INTERNAL_TYPE_BYTE ? + ClassConstants.EXTERNAL_TYPE_BYTE : + internalTypeChar == ClassConstants.INTERNAL_TYPE_CHAR ? + ClassConstants.EXTERNAL_TYPE_CHAR : + internalTypeChar == ClassConstants.INTERNAL_TYPE_SHORT ? + ClassConstants.EXTERNAL_TYPE_SHORT : + internalTypeChar == ClassConstants.INTERNAL_TYPE_INT ? + ClassConstants.EXTERNAL_TYPE_INT : + internalTypeChar == ClassConstants.INTERNAL_TYPE_FLOAT ? + ClassConstants.EXTERNAL_TYPE_FLOAT : + internalTypeChar == ClassConstants.INTERNAL_TYPE_LONG ? + ClassConstants.EXTERNAL_TYPE_LONG : + internalTypeChar == ClassConstants.INTERNAL_TYPE_DOUBLE ? + ClassConstants.EXTERNAL_TYPE_DOUBLE : + internalTypeChar == '%' ? + "%" : + internalTypeChar == ClassConstants.INTERNAL_TYPE_CLASS_START ? + externalClassName(internalType.substring(1, internalType.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_END))) : + null; + + if (externalType == null) + { + throw new IllegalArgumentException("Unknown type ["+internalType+"]"); + } + + // Append the array part, if any. + for (int count = 0; count < dimensionCount; count++) + { + externalType += ClassConstants.EXTERNAL_TYPE_ARRAY; + } + + return externalType; + } + + + /** + * Returns whether the given internal descriptor String represents a method + * descriptor. + * @param internalDescriptor the internal descriptor String, + * e.g. "<code>(II)Z</code>". + * @return <code>true</code> if the given String is a method descriptor, + * <code>false</code> otherwise. + */ + public static boolean isInternalMethodDescriptor(String internalDescriptor) + { + return internalDescriptor.charAt(0) == ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN; + } + + + /** + * Returns whether the given member String represents an external method + * name with arguments. + * @param externalMemberNameAndArguments the external member String, + * e.g. "<code>myField</code>" or + * e.g. "<code>myMethod(int,int)</code>". + * @return <code>true</code> if the given String refers to a method, + * <code>false</code> otherwise. + */ + public static boolean isExternalMethodNameAndArguments(String externalMemberNameAndArguments) + { + return externalMemberNameAndArguments.indexOf(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN) > 0; + } + + + /** + * Returns the name part of the given external method name and arguments. + * @param externalMethodNameAndArguments the external method name and arguments, + * e.g. "<code>myMethod(int,int)</code>". + * @return the name part of the String, e.g. "<code>myMethod</code>". + */ + public static String externalMethodName(String externalMethodNameAndArguments) + { + ExternalTypeEnumeration externalTypeEnumeration = + new ExternalTypeEnumeration(externalMethodNameAndArguments); + + return externalTypeEnumeration.methodName(); + } + + + /** + * Converts the given external method return type and name and arguments to + * an internal method descriptor. + * @param externalReturnType the external method return type, + * e.g. "<code>boolean</code>". + * @param externalMethodNameAndArguments the external method name and arguments, + * e.g. "<code>myMethod(int,int)</code>". + * @return the internal method descriptor, + * e.g. "<code>(II)Z</code>". + */ + public static String internalMethodDescriptor(String externalReturnType, + String externalMethodNameAndArguments) + { + StringBuffer internalMethodDescriptor = new StringBuffer(); + internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN); + + ExternalTypeEnumeration externalTypeEnumeration = + new ExternalTypeEnumeration(externalMethodNameAndArguments); + + while (externalTypeEnumeration.hasMoreTypes()) + { + internalMethodDescriptor.append(internalType(externalTypeEnumeration.nextType())); + } + + internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); + internalMethodDescriptor.append(internalType(externalReturnType)); + + return internalMethodDescriptor.toString(); + } + + + /** + * Converts the given external method return type and List of arguments to + * an internal method descriptor. + * @param externalReturnType the external method return type, + * e.g. "<code>boolean</code>". + * @param externalArguments the external method arguments, + * e.g. <code>{ "int", "int" }</code>. + * @return the internal method descriptor, + * e.g. "<code>(II)Z</code>". + */ + public static String internalMethodDescriptor(String externalReturnType, + List externalArguments) + { + StringBuffer internalMethodDescriptor = new StringBuffer(); + internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN); + + for (int index = 0; index < externalArguments.size(); index++) + { + internalMethodDescriptor.append(internalType((String)externalArguments.get(index))); + } + + internalMethodDescriptor.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); + internalMethodDescriptor.append(internalType(externalReturnType)); + + return internalMethodDescriptor.toString(); + } + + + /** + * Converts an internal field description into an external full field description. + * @param accessFlags the access flags of the field. + * @param fieldName the field name, + * e.g. "<code>myField</code>". + * @param internalFieldDescriptor the internal field descriptor, + * e.g. "<code>Z</code>". + * @return the external full field description, + * e.g. "<code>public boolean myField</code>". + */ + public static String externalFullFieldDescription(int accessFlags, + String fieldName, + String internalFieldDescriptor) + { + return externalFieldAccessFlags(accessFlags) + + externalType(internalFieldDescriptor) + + ' ' + + fieldName; + } + + + /** + * Converts an internal method description into an external full method description. + * @param internalClassName the internal name of the class of the method, + * e.g. "<code>mypackage/MyClass</code>". + * @param accessFlags the access flags of the method. + * @param internalMethodName the internal method name, + * e.g. "<code>myMethod</code>" or + * "<code><init></code>". + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(II)Z</code>". + * @return the external full method description, + * e.g. "<code>public boolean myMethod(int,int)</code>" or + * "<code>public MyClass(int,int)</code>". + */ + public static String externalFullMethodDescription(String internalClassName, + int accessFlags, + String internalMethodName, + String internalMethodDescriptor) + { + return externalMethodAccessFlags(accessFlags) + + externalMethodReturnTypeAndName(internalClassName, + internalMethodName, + internalMethodDescriptor) + + ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN + + externalMethodArguments(internalMethodDescriptor) + + ClassConstants.EXTERNAL_METHOD_ARGUMENTS_CLOSE; + } + + + /** + * Converts internal class access flags into an external access description. + * @param accessFlags the class access flags. + * @return the external class access description, + * e.g. "<code>public final </code>". + */ + public static String externalClassAccessFlags(int accessFlags) + { + return externalClassAccessFlags(accessFlags, ""); + } + + + /** + * Converts internal class access flags into an external access description. + * @param accessFlags the class access flags. + * @param prefix a prefix that is added to each access modifier. + * @return the external class access description, + * e.g. "<code>public final </code>". + */ + public static String externalClassAccessFlags(int accessFlags, String prefix) + { + if (accessFlags == 0) + { + return EMPTY_STRING; + } + + StringBuffer string = new StringBuffer(50); + + if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_ANNOTATTION) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ANNOTATION); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_INTERFACE) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_INTERFACE).append(' '); + } + else if ((accessFlags & ClassConstants.INTERNAL_ACC_ENUM) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ENUM).append(' '); + } + else if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ABSTRACT).append(' '); + } + + return string.toString(); + } + + + /** + * Converts internal field access flags into an external access description. + * @param accessFlags the field access flags. + * @return the external field access description, + * e.g. "<code>public volatile </code>". + */ + public static String externalFieldAccessFlags(int accessFlags) + { + return externalFieldAccessFlags(accessFlags, ""); + } + + + /** + * Converts internal field access flags into an external access description. + * @param accessFlags the field access flags. + * @param prefix a prefix that is added to each access modifier. + * @return the external field access description, + * e.g. "<code>public volatile </code>". + */ + public static String externalFieldAccessFlags(int accessFlags, String prefix) + { + if (accessFlags == 0) + { + return EMPTY_STRING; + } + + StringBuffer string = new StringBuffer(50); + + if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_PRIVATE) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PRIVATE).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_PROTECTED) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PROTECTED).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STATIC).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_VOLATILE) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_VOLATILE).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_TRANSIENT) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_TRANSIENT).append(' '); + } + + return string.toString(); + } + + + /** + * Converts internal method access flags into an external access description. + * @param accessFlags the method access flags. + * @return the external method access description, + * e.g. "<code>public synchronized </code>". + */ + public static String externalMethodAccessFlags(int accessFlags) + { + return externalMethodAccessFlags(accessFlags, ""); + } + + + /** + * Converts internal method access flags into an external access description. + * @param accessFlags the method access flags. + * @param prefix a prefix that is added to each access modifier. + * @return the external method access description, + * e.g. "public synchronized ". + */ + public static String externalMethodAccessFlags(int accessFlags, String prefix) + { + if (accessFlags == 0) + { + return EMPTY_STRING; + } + + StringBuffer string = new StringBuffer(50); + + if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PUBLIC).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_PRIVATE) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PRIVATE).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_PROTECTED) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_PROTECTED).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STATIC).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_FINAL).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_SYNCHRONIZED) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_SYNCHRONIZED).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_NATIVE) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_NATIVE).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_ABSTRACT).append(' '); + } + if ((accessFlags & ClassConstants.INTERNAL_ACC_STRICT) != 0) + { + string.append(prefix).append(ClassConstants.EXTERNAL_ACC_STRICT).append(' '); + } + + return string.toString(); + } + + + /** + * Converts an internal method descriptor into an external method return type. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(II)Z</code>". + * @return the external method return type, + * e.g. "<code>boolean</code>". + */ + public static String externalMethodReturnType(String internalMethodDescriptor) + { + return externalType(internalMethodReturnType(internalMethodDescriptor)); + } + + + /** + * Converts an internal class name, method name, and method descriptor to + * an external method return type and name. + * @param internalClassName the internal name of the class of the method, + * e.g. "<code>mypackage/MyClass</code>". + * @param internalMethodName the internal method name, + * e.g. "<code>myMethod</code>" or + * "<code><init></code>". + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(II)Z</code>". + * @return the external method return type and name, + * e.g. "<code>boolean myMethod</code>" or + * "<code>MyClass</code>". + */ + private static String externalMethodReturnTypeAndName(String internalClassName, + String internalMethodName, + String internalMethodDescriptor) + { + return internalMethodName.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ? + externalShortClassName(externalClassName(internalClassName)) : + (externalMethodReturnType(internalMethodDescriptor) + + ' ' + + internalMethodName); + } + + + /** + * Converts an internal method descriptor into an external method argument + * description. + * @param internalMethodDescriptor the internal method descriptor, + * e.g. "<code>(II)Z</code>". + * @return the external method argument description, + * e.g. "<code>int,int</code>". + */ + public static String externalMethodArguments(String internalMethodDescriptor) + { + StringBuffer externalMethodNameAndArguments = new StringBuffer(); + + InternalTypeEnumeration internalTypeEnumeration = + new InternalTypeEnumeration(internalMethodDescriptor); + + while (internalTypeEnumeration.hasMoreTypes()) + { + externalMethodNameAndArguments.append(externalType(internalTypeEnumeration.nextType())); + if (internalTypeEnumeration.hasMoreTypes()) + { + externalMethodNameAndArguments.append(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_SEPARATOR); + } + } + + return externalMethodNameAndArguments.toString(); + } + + + /** + * Returns the internal package name of the given internal class name. + * @param internalClassName the internal class name, + * e.g. "<code>java/lang/Object</code>". + * @return the internal package name, + * e.g. "<code>java/lang</code>". + */ + public static String internalPackageName(String internalClassName) + { + String internalPackagePrefix = internalPackagePrefix(internalClassName); + int length = internalPackagePrefix.length(); + return length > 0 ? + internalPackagePrefix.substring(0, length - 1) : + ""; + } + + + /** + * Returns the internal package prefix of the given internal class name. + * @param internalClassName the internal class name, + * e.g. "<code>java/lang/Object</code>". + * @return the internal package prefix, + * e.g. "<code>java/lang/</code>". + */ + public static String internalPackagePrefix(String internalClassName) + { + return internalClassName.substring(0, internalClassName.lastIndexOf(ClassConstants.INTERNAL_PACKAGE_SEPARATOR, + internalClassName.length() - 2) + 1); + } + + + /** + * Returns the external package name of the given external class name. + * @param externalClassName the external class name, + * e.g. "<code>java.lang.Object</code>". + * @return the external package name, + * e.g. "<code>java.lang</code>". + */ + public static String externalPackageName(String externalClassName) + { + String externalPackagePrefix = externalPackagePrefix(externalClassName); + int length = externalPackagePrefix.length(); + return length > 0 ? + externalPackagePrefix.substring(0, length - 1) : + ""; + } + + + /** + * Returns the external package prefix of the given external class name. + * @param externalClassName the external class name, + * e.g. "<code>java.lang.Object</code>". + * @return the external package prefix, + * e.g. "<code>java.lang.</code>". + */ + public static String externalPackagePrefix(String externalClassName) + { + return externalClassName.substring(0, externalClassName.lastIndexOf(ClassConstants.EXTERNAL_PACKAGE_SEPARATOR, + externalClassName.length() - 2) + 1); + } +} diff --git a/src/proguard/classfile/util/DescriptorClassEnumeration.java b/src/proguard/classfile/util/DescriptorClassEnumeration.java new file mode 100644 index 0000000..0bee2d5 --- /dev/null +++ b/src/proguard/classfile/util/DescriptorClassEnumeration.java @@ -0,0 +1,236 @@ +/* + * 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.classfile.util; + +import proguard.classfile.ClassConstants; + +import java.util.Stack; + +/** + * A <code>DescriptorClassEnumeration</code> provides an enumeration of all + * classes mentioned in a given descriptor or signature. + * + * @author Eric Lafortune + */ +public class DescriptorClassEnumeration +{ + private String descriptor; + + private int index; + private int nestingLevel; + private boolean isInnerClassName; + private String accumulatedClassName; + private Stack accumulatedClassNames; + + + /** + * Creates a new DescriptorClassEnumeration for the given descriptor. + */ + public DescriptorClassEnumeration(String descriptor) + { + this.descriptor = descriptor; + } + + + /** + * Returns the number of classes contained in the descriptor. This + * is the number of class names that the enumeration will return. + */ + public int classCount() + { + int count = 0; + + nextFluff(); + while (hasMoreClassNames()) + { + count++; + + nextClassName(); + nextFluff(); + } + + index = 0; + + return count; + } + + + /** + * Returns whether the enumeration can provide more class names from the + * descriptor. + */ + public boolean hasMoreClassNames() + { + return index < descriptor.length(); + } + + + /** + * Returns the next fluff (surrounding class names) from the descriptor. + */ + public String nextFluff() + { + int fluffStartIndex = index; + + // Find the first token marking the start of a class name 'L' or '.'. + loop: while (index < descriptor.length()) + { + switch (descriptor.charAt(index++)) + { + case ClassConstants.INTERNAL_TYPE_GENERIC_START: + { + nestingLevel++; + + // Make sure we have a stack. + if (accumulatedClassNames == null) + { + accumulatedClassNames = new Stack(); + } + + // Remember the accumulated class name. + accumulatedClassNames.push(accumulatedClassName); + + break; + } + case ClassConstants.INTERNAL_TYPE_GENERIC_END: + { + nestingLevel--; + + // Return to the accumulated class name outside the + // generic block. + accumulatedClassName = (String)accumulatedClassNames.pop(); + + continue loop; + } + case ClassConstants.INTERNAL_TYPE_GENERIC_BOUND: + { + continue loop; + } + case ClassConstants.INTERNAL_TYPE_CLASS_START: + { + // We've found the start of an ordinary class name. + nestingLevel += 2; + isInnerClassName = false; + break loop; + } + case ClassConstants.INTERNAL_TYPE_CLASS_END: + { + nestingLevel -= 2; + break; + } + case ClassConstants.EXTERNAL_INNER_CLASS_SEPARATOR: + { + // We've found the start of an inner class name in a signature. + isInnerClassName = true; + break loop; + } + case ClassConstants.INTERNAL_TYPE_GENERIC_VARIABLE_START: + { + // We've found the start of a type identifier. Skip to the end. + while (descriptor.charAt(index++) != ClassConstants.INTERNAL_TYPE_CLASS_END); + break; + } + } + + if (nestingLevel == 1 && + descriptor.charAt(index) != ClassConstants.INTERNAL_TYPE_GENERIC_END) + { + // We're at the start of a type parameter. Skip to the start + // of the bounds. + while (descriptor.charAt(index++) != ClassConstants.INTERNAL_TYPE_GENERIC_BOUND); + } + } + + return descriptor.substring(fluffStartIndex, index); + } + + + /** + * Returns the next class name from the descriptor. + */ + public String nextClassName() + { + int classNameStartIndex = index; + + // Find the first token marking the end of a class name '<' or ';'. + loop: while (true) + { + switch (descriptor.charAt(index)) + { + case ClassConstants.INTERNAL_TYPE_GENERIC_START: + case ClassConstants.INTERNAL_TYPE_CLASS_END: + case ClassConstants.EXTERNAL_INNER_CLASS_SEPARATOR: + { + break loop; + } + } + + index++; + } + + String className = descriptor.substring(classNameStartIndex, index); + + // Recompose the inner class name if necessary. + accumulatedClassName = isInnerClassName ? + accumulatedClassName + ClassConstants.INTERNAL_INNER_CLASS_SEPARATOR + className : + className; + + return accumulatedClassName; + } + + + /** + * Returns whether the most recently returned class name was a recomposed + * inner class name from a signature. + */ + public boolean isInnerClassName() + { + return isInnerClassName; + } + + + /** + * A main method for testing the class name enumeration. + */ + public static void main(String[] args) + { + try + { + for (int index = 0; index < args.length; index++) + { + String descriptor = args[index]; + + System.out.println("Descriptor ["+descriptor+"]"); + DescriptorClassEnumeration enumeration = new DescriptorClassEnumeration(descriptor); + System.out.println(" Fluff: ["+enumeration.nextFluff()+"]"); + while (enumeration.hasMoreClassNames()) + { + System.out.println(" Name: ["+enumeration.nextClassName()+"]"); + System.out.println(" Fluff: ["+enumeration.nextFluff()+"]"); + } + } + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } +} diff --git a/src/proguard/classfile/util/DynamicClassReferenceInitializer.java b/src/proguard/classfile/util/DynamicClassReferenceInitializer.java new file mode 100644 index 0000000..09ffdd0 --- /dev/null +++ b/src/proguard/classfile/util/DynamicClassReferenceInitializer.java @@ -0,0 +1,478 @@ +/* + * 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.classfile.util; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.util.StringMatcher; + +/** + * This InstructionVisitor initializes any constant <code>Class.forName</code> or + * <code>.class</code> references of all classes it visits. More specifically, + * it fills out the references of string constant pool entries that refer to a + * class in the program class pool or in the library class pool. + * <p> + * It optionally prints notes if on usage of + * <code>(SomeClass)Class.forName(variable).newInstance()</code>. + * <p> + * The class hierarchy must be initialized before using this visitor. + * + * @see ClassSuperHierarchyInitializer + * + * @author Eric Lafortune + */ +public class DynamicClassReferenceInitializer +extends SimplifiedVisitor +implements InstructionVisitor, + ConstantVisitor, + AttributeVisitor +{ + public static final int X = InstructionSequenceMatcher.X; + public static final int Y = InstructionSequenceMatcher.Y; + public static final int Z = InstructionSequenceMatcher.Z; + + public static final int A = InstructionSequenceMatcher.A; + public static final int B = InstructionSequenceMatcher.B; + public static final int C = InstructionSequenceMatcher.C; + public static final int D = InstructionSequenceMatcher.D; + + + private final Constant[] CLASS_FOR_NAME_CONSTANTS = new Constant[] + { + // 0 + new MethodrefConstant(1, 2, null, null), + new ClassConstant(3, null), + new NameAndTypeConstant(4, 5), + new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_CLASS_FOR_NAME), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_FOR_NAME), + + // 6 + new MethodrefConstant(1, 7, null, null), + new NameAndTypeConstant(8, 9), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_NEW_INSTANCE), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_NEW_INSTANCE), + + // 10 + new MethodrefConstant(1, 11, null, null), + new NameAndTypeConstant(12, 13), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_CLASS_GET_COMPONENT_TYPE), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_GET_COMPONENT_TYPE), + }; + + // Class.forName("SomeClass"). + private final Instruction[] CONSTANT_CLASS_FOR_NAME_INSTRUCTIONS = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, X), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0), + }; + + // (SomeClass)Class.forName(someName).newInstance(). + private final Instruction[] CLASS_FOR_NAME_CAST_INSTRUCTIONS = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 6), + new ConstantInstruction(InstructionConstants.OP_CHECKCAST, X), + }; + + +// private Constant[] DOT_CLASS_JAVAC_CONSTANTS = new Constant[] +// { +// new MethodrefConstant(A, 1, null, null), +// new NameAndTypeConstant(2, 3), +// new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_DOT_CLASS_JAVAC), +// new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JAVAC), +// }; + + private final Constant[] DOT_CLASS_JAVAC_CONSTANTS = new Constant[] + { + new MethodrefConstant(A, 1, null, null), + new NameAndTypeConstant(B, 2), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JAVAC), + }; + + // SomeClass.class = class$("SomeClass") (javac). + private final Instruction[] DOT_CLASS_JAVAC_INSTRUCTIONS = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, X), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0), + }; + + +// private Constant[] DOT_CLASS_JIKES_CONSTANTS = new Constant[] +// { +// new MethodrefConstant(A, 1, null, null), +// new NameAndTypeConstant(2, 3), +// new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_DOT_CLASS_JIKES), +// new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JIKES), +// }; + + private final Constant[] DOT_CLASS_JIKES_CONSTANTS = new Constant[] + { + new MethodrefConstant(A, 1, null, null), + new NameAndTypeConstant(B, 2), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JIKES), + }; + + // SomeClass.class = class("SomeClass", false) (jikes). + private final Instruction[] DOT_CLASS_JIKES_INSTRUCTIONS = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, X), + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0), + }; + + // return Class.forName(v0). + private final Instruction[] DOT_CLASS_JAVAC_IMPLEMENTATION_INSTRUCTIONS = new Instruction[] + { + new VariableInstruction(InstructionConstants.OP_ALOAD_0), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0), + new SimpleInstruction(InstructionConstants.OP_ARETURN), + }; + + // return Class.forName(v0), if (!v1) .getComponentType(). + private final Instruction[] DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS = new Instruction[] + { + new VariableInstruction(InstructionConstants.OP_ALOAD_0), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0), + new VariableInstruction(InstructionConstants.OP_ALOAD_1), + new BranchInstruction(InstructionConstants.OP_IFNE, +6), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 10), + new SimpleInstruction(InstructionConstants.OP_ARETURN), + }; + + // return Class.forName(v0).getComponentType(). + private final Instruction[] DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS2 = new Instruction[] + { + new VariableInstruction(InstructionConstants.OP_ALOAD_0), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, 0), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 10), + new SimpleInstruction(InstructionConstants.OP_ARETURN), + }; + + + private final ClassPool programClassPool; + private final ClassPool libraryClassPool; + private final WarningPrinter missingNotePrinter; + private final WarningPrinter dependencyWarningPrinter; + private final WarningPrinter notePrinter; + private final StringMatcher noteExceptionMatcher; + + + private final InstructionSequenceMatcher constantClassForNameMatcher = + new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS, + CONSTANT_CLASS_FOR_NAME_INSTRUCTIONS); + + private final InstructionSequenceMatcher classForNameCastMatcher = + new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS, + CLASS_FOR_NAME_CAST_INSTRUCTIONS); + + private final InstructionSequenceMatcher dotClassJavacMatcher = + new InstructionSequenceMatcher(DOT_CLASS_JAVAC_CONSTANTS, + DOT_CLASS_JAVAC_INSTRUCTIONS); + + private final InstructionSequenceMatcher dotClassJikesMatcher = + new InstructionSequenceMatcher(DOT_CLASS_JIKES_CONSTANTS, + DOT_CLASS_JIKES_INSTRUCTIONS); + + private final InstructionSequenceMatcher dotClassJavacImplementationMatcher = + new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS, + DOT_CLASS_JAVAC_IMPLEMENTATION_INSTRUCTIONS); + + private final InstructionSequenceMatcher dotClassJikesImplementationMatcher = + new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS, + DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS); + + private final InstructionSequenceMatcher dotClassJikesImplementationMatcher2 = + new InstructionSequenceMatcher(CLASS_FOR_NAME_CONSTANTS, + DOT_CLASS_JIKES_IMPLEMENTATION_INSTRUCTIONS2); + + + // A field acting as a return variable for the visitors. + private boolean isClassForNameInvocation; + + + /** + * Creates a new DynamicClassReferenceInitializer that optionally prints + * warnings and notes, with optional class specifications for which never + * to print notes. + */ + public DynamicClassReferenceInitializer(ClassPool programClassPool, + ClassPool libraryClassPool, + WarningPrinter missingNotePrinter, + WarningPrinter dependencyWarningPrinter, + WarningPrinter notePrinter, + StringMatcher noteExceptionMatcher) + { + this.programClassPool = programClassPool; + this.libraryClassPool = libraryClassPool; + this.missingNotePrinter = missingNotePrinter; + this.dependencyWarningPrinter = dependencyWarningPrinter; + this.notePrinter = notePrinter; + this.noteExceptionMatcher = noteExceptionMatcher; + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + // Try to match the Class.forName("SomeClass") construct. + instruction.accept(clazz, method, codeAttribute, offset, + constantClassForNameMatcher); + + // Did we find a match? + if (constantClassForNameMatcher.isMatching()) + { + // Fill out the matched string constant. + clazz.constantPoolEntryAccept(constantClassForNameMatcher.matchedConstantIndex(X), this); + + // Don't look for the dynamic construct. + classForNameCastMatcher.reset(); + } + + // Try to match the (SomeClass)Class.forName(someName).newInstance() + // construct. + instruction.accept(clazz, method, codeAttribute, offset, + classForNameCastMatcher); + + // Did we find a match? + if (classForNameCastMatcher.isMatching()) + { + // Print out a note about the construct. + clazz.constantPoolEntryAccept(classForNameCastMatcher.matchedConstantIndex(X), this); + } + + // Try to match the javac .class construct. + instruction.accept(clazz, method, codeAttribute, offset, + dotClassJavacMatcher); + + // Did we find a match? + if (dotClassJavacMatcher.isMatching() && + isDotClassMethodref(clazz, dotClassJavacMatcher.matchedConstantIndex(0))) + { + // Fill out the matched string constant. + clazz.constantPoolEntryAccept(dotClassJavacMatcher.matchedConstantIndex(X), this); + } + + // Try to match the jikes .class construct. + instruction.accept(clazz, method, codeAttribute, offset, + dotClassJikesMatcher); + + // Did we find a match? + if (dotClassJikesMatcher.isMatching() && + isDotClassMethodref(clazz, dotClassJikesMatcher.matchedConstantIndex(0))) + { + // Fill out the matched string constant. + clazz.constantPoolEntryAccept(dotClassJikesMatcher.matchedConstantIndex(X), this); + } + } + + + // Implementations for ConstantVisitor. + + /** + * Fills out the link to the referenced class. + */ + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + // Save a reference to the corresponding class. + String externalClassName = stringConstant.getString(clazz); + String internalClassName = ClassUtil.internalClassName(externalClassName); + + stringConstant.referencedClass = findClass(clazz.getName(), internalClassName); + } + + + /** + * Prints out a note about the class cast to this class, if applicable. + */ + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Print out a note about the class cast. + if (noteExceptionMatcher == null || + !noteExceptionMatcher.matches(classConstant.getName(clazz))) + { + notePrinter.print(clazz.getName(), + classConstant.getName(clazz), + "Note: " + + ClassUtil.externalClassName(clazz.getName()) + + " calls '(" + + ClassUtil.externalClassName(classConstant.getName(clazz)) + + ")Class.forName(variable).newInstance()'"); + } + } + + + /** + * Checks whether the referenced method is a .class method. + */ + public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + { + String methodType = methodrefConstant.getType(clazz); + + // Do the method's class and type match? + if (methodType.equals(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JAVAC) || + methodType.equals(ClassConstants.INTERNAL_METHOD_TYPE_DOT_CLASS_JIKES)) + { + String methodName = methodrefConstant.getName(clazz); + + // Does the method's name match one of the special names? + isClassForNameInvocation = + methodName.equals(ClassConstants.INTERNAL_METHOD_NAME_DOT_CLASS_JAVAC) || + methodName.equals(ClassConstants.INTERNAL_METHOD_NAME_DOT_CLASS_JIKES); + + if (isClassForNameInvocation) + { + return; + } + + String className = methodrefConstant.getClassName(clazz); + + // Note that we look for the class by name, since the referenced + // class has not been initialized yet. + Clazz referencedClass = programClassPool.getClass(className); + if (referencedClass != null) + { + // Check if the code of the referenced method is .class code. + // Note that we look for the method by name and type, since the + // referenced method has not been initialized yet. + referencedClass.methodAccept(methodName, + methodType, + new AllAttributeVisitor(this)); + } + } + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Check whether this is class$(String), as generated by javac, or + // class(String, boolean), as generated by jikes, or an optimized + // version. + isClassForNameInvocation = + isDotClassMethodCode(clazz, method, codeAttribute, + dotClassJavacImplementationMatcher, 5) || + isDotClassMethodCode(clazz, method, codeAttribute, + dotClassJikesImplementationMatcher, 12) || + isDotClassMethodCode(clazz, method, codeAttribute, + dotClassJikesImplementationMatcher2, 8); + } + + + // Small utility methods. + + /** + * Returns whether the given method reference corresponds to a .class + * method, as generated by javac or by jikes. + */ + private boolean isDotClassMethodref(Clazz clazz, int methodrefConstantIndex) + { + isClassForNameInvocation = false; + + // Check if the code of the referenced method is .class code. + clazz.constantPoolEntryAccept(methodrefConstantIndex, this); + + return isClassForNameInvocation; + } + + + /** + * Returns whether the first whether the first instructions of the + * given code attribute match with the given instruction matcher. + */ + private boolean isDotClassMethodCode(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + InstructionSequenceMatcher codeMatcher, + int codeLength) + { + // Check the minimum code length. + if (codeAttribute.u4codeLength < codeLength) + { + return false; + } + + // Check the actual instructions. + codeMatcher.reset(); + codeAttribute.instructionsAccept(clazz, method, 0, codeLength, codeMatcher); + return codeMatcher.isMatching(); + } + + + /** + * 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) + { + // Ignore any primitive array types. + if (ClassUtil.isInternalArrayType(name) && + !ClassUtil.isInternalClassType(name)) + { + return null; + } + + // 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 && + missingNotePrinter != null) + { + // We didn't find the superclass or interface. Print a note. + missingNotePrinter.print(referencingClassName, + name, + "Note: " + + ClassUtil.externalClassName(referencingClassName) + + ": can't find dynamically 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 dynamically on program class " + + ClassUtil.externalClassName(name)); + } + + return clazz; + } +} diff --git a/src/proguard/classfile/util/DynamicMemberReferenceInitializer.java b/src/proguard/classfile/util/DynamicMemberReferenceInitializer.java new file mode 100644 index 0000000..1f57708 --- /dev/null +++ b/src/proguard/classfile/util/DynamicMemberReferenceInitializer.java @@ -0,0 +1,604 @@ +/* + * 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.classfile.util; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.visitor.*; +import proguard.util.StringMatcher; + +/** + * This InstructionVisitor initializes any constant + * <code>Class.get[Declared]{Field,Method}</code> references of all instructions + * it visits. More specifically, it fills out the references of string constant + * pool entries that refer to a class member in the program class pool or in the + * library class pool. + * <p> + * It optionally prints notes if on usage of + * <code>(SomeClass)Class.forName(variable).newInstance()</code>. + * <p> + * The class hierarchy and references must be initialized before using this + * visitor. + * + * @see ClassSuperHierarchyInitializer + * @see ClassReferenceInitializer + * + * @author Eric Lafortune + */ +public class DynamicMemberReferenceInitializer +extends SimplifiedVisitor +implements InstructionVisitor, + ConstantVisitor, + AttributeVisitor, + MemberVisitor +{ + public static final int X = InstructionSequenceMatcher.X; + public static final int Y = InstructionSequenceMatcher.Y; + public static final int Z = InstructionSequenceMatcher.Z; + + public static final int A = InstructionSequenceMatcher.A; + public static final int B = InstructionSequenceMatcher.B; + public static final int C = InstructionSequenceMatcher.C; + public static final int D = InstructionSequenceMatcher.D; + + + private final Constant[] GET_FIELD_CONSTANTS = new Constant[] + { + new MethodrefConstant(1, 2, null, null), + new ClassConstant(3, null), + new NameAndTypeConstant(4, 5), + new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_CLASS_GET_FIELD), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_GET_FIELD), + }; + + private final Constant[] GET_DECLARED_FIELD_CONSTANTS = new Constant[] + { + new MethodrefConstant(1, 2, null, null), + new ClassConstant(3, null), + new NameAndTypeConstant(4, 5), + new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_CLASS_GET_DECLARED_FIELD), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_GET_DECLARED_FIELD), + }; + + private final Constant[] GET_METHOD_CONSTANTS = new Constant[] + { + new MethodrefConstant(1, 2, null, null), + new ClassConstant(3, null), + new NameAndTypeConstant(4, 5), + new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_CLASS_GET_METHOD), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_GET_METHOD), + }; + + private final Constant[] GET_DECLARED_METHOD_CONSTANTS = new Constant[] + { + new MethodrefConstant(1, 2, null, null), + new ClassConstant(3, null), + new NameAndTypeConstant(4, 5), + new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_LANG_CLASS), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_CLASS_GET_DECLARED_METHOD), + new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CLASS_GET_DECLARED_METHOD), + }; + + // SomeClass.class.get[Declared]Field("someField"). + private final Instruction[] CONSTANT_GET_FIELD_INSTRUCTIONS = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, X), + new ConstantInstruction(InstructionConstants.OP_LDC, Y), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), + }; + + // SomeClass.class.get[Declared]Method("someMethod", new Class[] {}). + private final Instruction[] CONSTANT_GET_METHOD_INSTRUCTIONS0 = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, X), + new ConstantInstruction(InstructionConstants.OP_LDC, Y), + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new ConstantInstruction(InstructionConstants.OP_ANEWARRAY, 1), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), + }; + + // SomeClass.class.get[Declared]Method("someMethod", new Class[] { A.class }). + private final Instruction[] CONSTANT_GET_METHOD_INSTRUCTIONS1 = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, X), + new ConstantInstruction(InstructionConstants.OP_LDC, Y), + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + new ConstantInstruction(InstructionConstants.OP_ANEWARRAY, 1), + new SimpleInstruction(InstructionConstants.OP_DUP), + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new SimpleInstruction(InstructionConstants.OP_AASTORE), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), + }; + + // SomeClass.class.get[Declared]Method("someMethod", new Class[] { A.class, B.class }). + private final Instruction[] CONSTANT_GET_METHOD_INSTRUCTIONS2 = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, X), + new ConstantInstruction(InstructionConstants.OP_LDC, Y), + new SimpleInstruction(InstructionConstants.OP_ICONST_2), + new ConstantInstruction(InstructionConstants.OP_ANEWARRAY, 1), + new SimpleInstruction(InstructionConstants.OP_DUP), + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new SimpleInstruction(InstructionConstants.OP_AASTORE), + new SimpleInstruction(InstructionConstants.OP_DUP), + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new SimpleInstruction(InstructionConstants.OP_AASTORE), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), + }; + + // get[Declared]Field("someField"). + private final Instruction[] GET_FIELD_INSTRUCTIONS = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, Y), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), + }; + + // get[Declared]Method("someMethod", new Class[] {}). + private final Instruction[] GET_METHOD_INSTRUCTIONS0 = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, Y), + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new ConstantInstruction(InstructionConstants.OP_ANEWARRAY, 1), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), + }; + + // get[Declared]Method("someMethod", new Class[] { A.class }). + private final Instruction[] GET_METHOD_INSTRUCTIONS1 = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, Y), + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + new ConstantInstruction(InstructionConstants.OP_ANEWARRAY, 1), + new SimpleInstruction(InstructionConstants.OP_DUP), + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new SimpleInstruction(InstructionConstants.OP_AASTORE), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), + }; + + // get[Declared]Method("someMethod", new Class[] { A.class, B.class }). + private final Instruction[] GET_METHOD_INSTRUCTIONS2 = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_LDC, Y), + new SimpleInstruction(InstructionConstants.OP_ICONST_2), + new ConstantInstruction(InstructionConstants.OP_ANEWARRAY, 1), + new SimpleInstruction(InstructionConstants.OP_DUP), + new SimpleInstruction(InstructionConstants.OP_ICONST_0), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new SimpleInstruction(InstructionConstants.OP_AASTORE), + new SimpleInstruction(InstructionConstants.OP_DUP), + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new SimpleInstruction(InstructionConstants.OP_AASTORE), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, 0), + }; + + + private final ClassPool programClassPool; + private final ClassPool libraryClassPool; + private final WarningPrinter notePrinter; + private final StringMatcher noteFieldExceptionMatcher; + private final StringMatcher noteMethodExceptionMatcher; + + + private final InstructionSequenceMatcher constantGetFieldMatcher = + new InstructionSequenceMatcher(GET_FIELD_CONSTANTS, + CONSTANT_GET_FIELD_INSTRUCTIONS); + + private final InstructionSequenceMatcher constantGetDeclaredFieldMatcher = + new InstructionSequenceMatcher(GET_DECLARED_FIELD_CONSTANTS, + CONSTANT_GET_FIELD_INSTRUCTIONS); + + private final InstructionSequenceMatcher constantGetMethodMatcher0 = + new InstructionSequenceMatcher(GET_METHOD_CONSTANTS, + CONSTANT_GET_METHOD_INSTRUCTIONS0); + + private final InstructionSequenceMatcher constantGetDeclaredMethodMatcher0 = + new InstructionSequenceMatcher(GET_DECLARED_METHOD_CONSTANTS, + CONSTANT_GET_METHOD_INSTRUCTIONS0); + + private final InstructionSequenceMatcher constantGetMethodMatcher1 = + new InstructionSequenceMatcher(GET_METHOD_CONSTANTS, + CONSTANT_GET_METHOD_INSTRUCTIONS1); + + private final InstructionSequenceMatcher constantGetDeclaredMethodMatcher1 = + new InstructionSequenceMatcher(GET_DECLARED_METHOD_CONSTANTS, + CONSTANT_GET_METHOD_INSTRUCTIONS1); + + private final InstructionSequenceMatcher constantGetMethodMatcher2 = + new InstructionSequenceMatcher(GET_METHOD_CONSTANTS, + CONSTANT_GET_METHOD_INSTRUCTIONS2); + + private final InstructionSequenceMatcher constantGetDeclaredMethodMatcher2 = + new InstructionSequenceMatcher(GET_DECLARED_METHOD_CONSTANTS, + CONSTANT_GET_METHOD_INSTRUCTIONS2); + + private final InstructionSequenceMatcher getFieldMatcher = + new InstructionSequenceMatcher(GET_FIELD_CONSTANTS, + GET_FIELD_INSTRUCTIONS); + + private final InstructionSequenceMatcher getDeclaredFieldMatcher = + new InstructionSequenceMatcher(GET_DECLARED_FIELD_CONSTANTS, + GET_FIELD_INSTRUCTIONS); + + private final InstructionSequenceMatcher getMethodMatcher0 = + new InstructionSequenceMatcher(GET_METHOD_CONSTANTS, + GET_METHOD_INSTRUCTIONS0); + + private final InstructionSequenceMatcher getDeclaredMethodMatcher0 = + new InstructionSequenceMatcher(GET_DECLARED_METHOD_CONSTANTS, + GET_METHOD_INSTRUCTIONS0); + + private final InstructionSequenceMatcher getMethodMatcher1 = + new InstructionSequenceMatcher(GET_METHOD_CONSTANTS, + GET_METHOD_INSTRUCTIONS1); + + private final InstructionSequenceMatcher getDeclaredMethodMatcher1 = + new InstructionSequenceMatcher(GET_DECLARED_METHOD_CONSTANTS, + GET_METHOD_INSTRUCTIONS1); + + private final InstructionSequenceMatcher getMethodMatcher2 = + new InstructionSequenceMatcher(GET_METHOD_CONSTANTS, + GET_METHOD_INSTRUCTIONS2); + + private final InstructionSequenceMatcher getDeclaredMethodMatcher2 = + new InstructionSequenceMatcher(GET_DECLARED_METHOD_CONSTANTS, + GET_METHOD_INSTRUCTIONS2); + + private final MemberFinder memberFinder = new MemberFinder(); + + + // Fields acting as parameters for the visitors. + private Clazz referencedClass; + private boolean isDeclared; + private boolean isField; + + + + /** + * Creates a new DynamicMemberReferenceInitializer. + */ + public DynamicMemberReferenceInitializer(ClassPool programClassPool, + ClassPool libraryClassPool, + WarningPrinter notePrinter, + StringMatcher noteFieldExceptionMatcher, + StringMatcher noteMethodExceptionMatcher) + { + this.programClassPool = programClassPool; + this.libraryClassPool = libraryClassPool; + this.notePrinter = notePrinter; + this.noteFieldExceptionMatcher = noteFieldExceptionMatcher; + this.noteMethodExceptionMatcher = noteMethodExceptionMatcher; + } + + + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + // Try to match the SomeClass.class.getField("someField") construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetFieldMatcher, + getFieldMatcher, true, false); + + // Try to match the SomeClass.class.getDeclaredField("someField") construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetDeclaredFieldMatcher, + getDeclaredFieldMatcher, true, true); + + // Try to match the SomeClass.class.getMethod("someMethod", new Class[] + // {}) construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetMethodMatcher0, + getMethodMatcher0, false, false); + + // Try to match the SomeClass.class.getDeclaredMethod("someMethod", + // new Class[] {}) construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetDeclaredMethodMatcher0, + getDeclaredMethodMatcher0, false, true); + + // Try to match the SomeClass.class.getMethod("someMethod", new Class[] + // { A.class }) construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetMethodMatcher1, + getMethodMatcher1, false, false); + + // Try to match the SomeClass.class.getDeclaredMethod("someMethod", + // new Class[] { A.class }) construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetDeclaredMethodMatcher1, + getDeclaredMethodMatcher1, false, true); + + // Try to match the SomeClass.class.getMethod("someMethod", new Class[] + // { A.class, B.class }) construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetMethodMatcher2, + getMethodMatcher2, false, false); + + // Try to match the SomeClass.class.getDeclaredMethod("someMethod", + // new Class[] { A.class, B.class }) construct. + matchGetMember(clazz, method, codeAttribute, offset, instruction, + constantGetDeclaredMethodMatcher2, + getDeclaredMethodMatcher2, false, true); + } + + + /** + * Tries to match the next instruction and fills out the string constant + * or prints out a note accordingly. + */ + private void matchGetMember(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int offset, + Instruction instruction, + InstructionSequenceMatcher constantSequenceMatcher, + InstructionSequenceMatcher variableSequenceMatcher, + boolean isField, + boolean isDeclared) + { + // Try to match the next instruction in the constant sequence. + instruction.accept(clazz, method, codeAttribute, offset, + constantSequenceMatcher); + + // Did we find a match to fill out the string constant? + if (constantSequenceMatcher.isMatching()) + { + this.isField = isField; + this.isDeclared = isDeclared; + + // Get the member's class. + clazz.constantPoolEntryAccept(constantSequenceMatcher.matchedConstantIndex(X), this); + + // Fill out the matched string constant. + clazz.constantPoolEntryAccept(constantSequenceMatcher.matchedConstantIndex(Y), this); + + // Don't look for the dynamic construct. + variableSequenceMatcher.reset(); + } + + // Try to match the next instruction in the variable sequence. + instruction.accept(clazz, method, codeAttribute, offset, + variableSequenceMatcher); + + // Did we find a match to print out a note? + if (variableSequenceMatcher.isMatching()) + { + // Print out a note about the dynamic invocation. + printDynamicInvocationNote(clazz, + variableSequenceMatcher, + isField, + isDeclared); + } + } + + + // Implementations for ConstantVisitor. + + /** + * Remembers the referenced class. + */ + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Remember the referenced class. + referencedClass = ClassUtil.isInternalArrayType(classConstant.getName(clazz)) ? + null : + classConstant.referencedClass; + } + + + /** + * Fills out the link to the referenced class member. + */ + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + if (referencedClass != null) + { + String name = stringConstant.getString(clazz); + + // See if we can find the referenced class member locally, or + // somewhere in the hierarchy. + Member referencedMember = isDeclared ? isField ? + (Member)referencedClass.findField(name, null) : + (Member)referencedClass.findMethod(name, null) : + (Member)memberFinder.findMember(clazz, + referencedClass, + name, + null, + isField); + if (referencedMember != null) + { + stringConstant.referencedMember = referencedMember; + stringConstant.referencedClass = isDeclared ? + referencedClass : + memberFinder.correspondingClass(); + } + } + } + + + // Small utility methods. + + /** + * Prints out a note on the matched dynamic invocation, if necessary. + */ + private void printDynamicInvocationNote(Clazz clazz, + InstructionSequenceMatcher noteSequenceMatcher, + boolean isField, + boolean isDeclared) + { + // Print out a note about the dynamic invocation. + if (notePrinter != null && + notePrinter.accepts(clazz.getName())) + { + // Is the class member name in the list of exceptions? + StringMatcher noteExceptionMatcher = isField ? + noteFieldExceptionMatcher : + noteMethodExceptionMatcher; + + int memberNameIndex = noteSequenceMatcher.matchedConstantIndex(Y); + String memberName = clazz.getStringString(memberNameIndex); + + if (noteExceptionMatcher == null || + !noteExceptionMatcher.matches(memberName)) + { + // Compose the external member name and partial descriptor. + String externalMemberDescription = memberName; + + if (!isField) + { + externalMemberDescription += '('; + for (int count = 0; count < 2; count++) + { + int memberArgumentIndex = noteSequenceMatcher.matchedConstantIndex(A + count); + if (memberArgumentIndex > 0) + { + if (count > 0) + { + externalMemberDescription += ','; + } + String className = clazz.getClassName(memberArgumentIndex); + externalMemberDescription += ClassUtil.isInternalArrayType(className) ? + ClassUtil.externalType(className) : + ClassUtil.externalClassName(className); + } + } + externalMemberDescription += ')'; + } + + // Print out the actual note. + notePrinter.print(clazz.getName(), + "Note: " + + ClassUtil.externalClassName(clazz.getName()) + + " accesses a " + + (isDeclared ? "declared " : "") + + (isField ? "field" : "method") + + " '" + + externalMemberDescription + + "' dynamically"); + + // Print out notes about potential candidates. + ClassVisitor classVisitor; + + if (isField) + { + classVisitor = + new AllFieldVisitor( + new MemberNameFilter(memberName, this)); + } + else + { + // Compose the partial method descriptor. + String methodDescriptor = "("; + for (int count = 0; count < 2; count++) + { + int memberArgumentIndex = noteSequenceMatcher.matchedConstantIndex(A + count); + if (memberArgumentIndex > 0) + { + if (count > 0) + { + methodDescriptor += ','; + } + String className = clazz.getClassName(memberArgumentIndex); + methodDescriptor += ClassUtil.isInternalArrayType(className) ? + className : + ClassUtil.internalTypeFromClassName(className); + } + } + methodDescriptor += ")L///;"; + + classVisitor = + new AllMethodVisitor( + new MemberNameFilter(memberName, + new MemberDescriptorFilter(methodDescriptor, this))); + } + + programClassPool.classesAcceptAlphabetically(classVisitor); + libraryClassPool.classesAcceptAlphabetically(classVisitor); + } + } + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + if (notePrinter.accepts(programClass.getName())) + { + System.out.println(" Maybe this is program field '" + + ClassUtil.externalFullClassDescription(0, programClass.getName()) + + " { " + + ClassUtil.externalFullFieldDescription(0, programField.getName(programClass), programField.getDescriptor(programClass)) + + "; }'"); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (notePrinter.accepts(programClass.getName())) + { + System.out.println(" Maybe this is program method '" + + ClassUtil.externalFullClassDescription(0, programClass.getName()) + + " { " + + ClassUtil.externalFullMethodDescription(null, 0, programMethod.getName(programClass), programMethod.getDescriptor(programClass)) + + "; }'"); + } + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + if (notePrinter.accepts(libraryClass.getName())) + { + System.out.println(" Maybe this is library field '" + + ClassUtil.externalFullClassDescription(0, libraryClass.getName()) + + " { " + + ClassUtil.externalFullFieldDescription(0, libraryField.getName(libraryClass), libraryField.getDescriptor(libraryClass)) + + "; }'"); + } + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + if (notePrinter.accepts(libraryClass.getName())) + { + System.out.println(" Maybe this is library method '" + + ClassUtil.externalFullClassDescription(0, libraryClass.getName()) + + " { " + + ClassUtil.externalFullMethodDescription(null, 0, libraryMethod.getName(libraryClass), libraryMethod.getDescriptor(libraryClass)) + + "; }'"); + } + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/util/ExternalTypeEnumeration.java b/src/proguard/classfile/util/ExternalTypeEnumeration.java new file mode 100644 index 0000000..6371888 --- /dev/null +++ b/src/proguard/classfile/util/ExternalTypeEnumeration.java @@ -0,0 +1,106 @@ +/* + * 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.classfile.util; + +import proguard.classfile.ClassConstants; + + +/** + * An <code>ExternalTypeEnumeration</code> provides an enumeration of all + * types listed in a given external descriptor string. The method name can + * be retrieved separately. + * <p> + * A <code>ExternalTypeEnumeration</code> object can be reused for processing + * different subsequent descriptors, by means of the <code>setDescriptor</code> + * method. + * + * @author Eric Lafortune + */ +public class ExternalTypeEnumeration +{ + private String descriptor; + private int index; + + + public ExternalTypeEnumeration(String descriptor) + { + setDescriptor(descriptor); + } + + + ExternalTypeEnumeration() + { + } + + + void setDescriptor(String descriptor) + { + this.descriptor = descriptor; + + reset(); + } + + + public void reset() + { + index = descriptor.indexOf(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN) + 1; + + if (index < 1) + { + throw new IllegalArgumentException("Missing opening parenthesis in descriptor ["+descriptor+"]"); + } + } + + + public boolean hasMoreTypes() + { + return index < descriptor.length() - 1; + } + + + public String nextType() + { + int startIndex = index; + + // Find the next separating comma. + index = descriptor.indexOf(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_SEPARATOR, + startIndex); + + // Otherwise find the closing parenthesis. + if (index < 0) + { + index = descriptor.indexOf(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_CLOSE, + startIndex); + if (index < 0) + { + throw new IllegalArgumentException("Missing closing parenthesis in descriptor ["+descriptor+"]"); + } + } + + return descriptor.substring(startIndex, index++).trim(); + } + + + public String methodName() + { + return descriptor.substring(0, descriptor.indexOf(ClassConstants.EXTERNAL_METHOD_ARGUMENTS_OPEN)).trim(); + } +} diff --git a/src/proguard/classfile/util/InstructionSequenceMatcher.java b/src/proguard/classfile/util/InstructionSequenceMatcher.java new file mode 100644 index 0000000..8a689d5 --- /dev/null +++ b/src/proguard/classfile/util/InstructionSequenceMatcher.java @@ -0,0 +1,634 @@ +/* + * 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.classfile.util; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; + +/** + * This InstructionVisitor checks whether a given pattern instruction sequence + * occurs in the instructions that are visited. The arguments of the + * instruction sequence can be wildcards that are matched. + * + * @author Eric Lafortune + */ +public class InstructionSequenceMatcher +extends SimplifiedVisitor +implements InstructionVisitor, + ConstantVisitor +{ + /* + private static boolean DEBUG = false; + public static boolean DEBUG_MORE = false; + /*/ + private static final boolean DEBUG = false; + private static final boolean DEBUG_MORE = false; + //*/ + + public static final int X = 0x40000000; + public static final int Y = 0x40000001; + public static final int Z = 0x40000002; + + public static final int A = 0x40000003; + public static final int B = 0x40000004; + public static final int C = 0x40000005; + public static final int D = 0x40000006; + + + private final Constant[] patternConstants; + private final Instruction[] patternInstructions; + + private boolean matching; + private boolean matchingAnyWildCards; + private int patternInstructionIndex; + private final int[] matchedInstructionOffsets; + private int matchedArgumentFlags; + private final int[] matchedArguments = new int[7]; + private long matchedConstantFlags; + private final int[] matchedConstantIndices; + + // Fields acting as a parameter and a return value for visitor methods. + private Constant patternConstant; + private boolean matchingConstant; + + + /** + * Creates a new InstructionSequenceMatcher. + * @param patternConstants any constants referenced by the pattern + * instruction. + * @param patternInstructions the pattern instruction sequence. + */ + public InstructionSequenceMatcher(Constant[] patternConstants, + Instruction[] patternInstructions) + { + this.patternConstants = patternConstants; + this.patternInstructions = patternInstructions; + + matchedInstructionOffsets = new int[patternInstructions.length]; + matchedConstantIndices = new int[patternConstants.length]; + } + + + /** + * Starts matching from the first instruction again next time. + */ + public void reset() + { + patternInstructionIndex = 0; + matchedArgumentFlags = 0; + matchedConstantFlags = 0L; + } + + + public boolean isMatching() + { + return matching; + } + + + public boolean isMatchingAnyWildcards() + { + return matchingAnyWildCards; + } + + + public int instructionCount() + { + return patternInstructions.length; + } + + + public int matchedInstructionOffset(int index) + { + return matchedInstructionOffsets[index]; + } + + + public int matchedArgument(int argument) + { + int argumentIndex = argument - X; + return argumentIndex < 0 ? + argument : + matchedArguments[argumentIndex]; + } + + + public int[] matchedArguments(int[] arguments) + { + int[] matchedArguments = new int[arguments.length]; + + for (int index = 0; index < arguments.length; index++) + { + matchedArguments[index] = matchedArgument(arguments[index]); + } + + return matchedArguments; + } + + + public int matchedConstantIndex(int constantIndex) + { + int argumentIndex = constantIndex - X; + return argumentIndex < 0 ? + matchedConstantIndices[constantIndex] : + matchedArguments[argumentIndex]; + } + + + public int matchedBranchOffset(int offset, int branchOffset) + { + int argumentIndex = branchOffset - X; + return argumentIndex < 0 ? + branchOffset : + matchedArguments[argumentIndex] - offset; + } + + + public int[] matchedJumpOffsets(int offset, int[] jumpOffsets) + { + int[] matchedJumpOffsets = new int[jumpOffsets.length]; + + for (int index = 0; index < jumpOffsets.length; index++) + { + matchedJumpOffsets[index] = matchedBranchOffset(offset, + jumpOffsets[index]); + } + + return matchedJumpOffsets; + } + + + // Implementations for InstructionVisitor. + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + Instruction patternInstruction = patternInstructions[patternInstructionIndex]; + + // Check if the instruction matches the next instruction in the sequence. + boolean condition = + matchingOpcodes(simpleInstruction, patternInstruction) && + matchingArguments(simpleInstruction.constant, + ((SimpleInstruction)patternInstruction).constant); + + // Check if the instruction sequence is matching now. + checkMatch(condition, + clazz, + method, + codeAttribute, + offset, + simpleInstruction); + } + + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + Instruction patternInstruction = patternInstructions[patternInstructionIndex]; + + // Check if the instruction matches the next instruction in the sequence. + boolean condition = + matchingOpcodes(variableInstruction, patternInstruction) && + matchingArguments(variableInstruction.variableIndex, + ((VariableInstruction)patternInstruction).variableIndex) && + matchingArguments(variableInstruction.constant, + ((VariableInstruction)patternInstruction).constant); + + // Check if the instruction sequence is matching now. + checkMatch(condition, + clazz, + method, + codeAttribute, + offset, + variableInstruction); + } + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + Instruction patternInstruction = patternInstructions[patternInstructionIndex]; + + // Check if the instruction matches the next instruction in the sequence. + boolean condition = + matchingOpcodes(constantInstruction, patternInstruction) && + matchingConstantIndices(clazz, + constantInstruction.constantIndex, + ((ConstantInstruction)patternInstruction).constantIndex) && + matchingArguments(constantInstruction.constant, + ((ConstantInstruction)patternInstruction).constant); + + // Check if the instruction sequence is matching now. + checkMatch(condition, + clazz, + method, + codeAttribute, + offset, + constantInstruction); + } + + + public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) + { + Instruction patternInstruction = patternInstructions[patternInstructionIndex]; + + // Check if the instruction matches the next instruction in the from + // sequence. + boolean condition = + matchingOpcodes(branchInstruction, patternInstruction) && + matchingBranchOffsets(offset, + branchInstruction.branchOffset, + ((BranchInstruction)patternInstruction).branchOffset); + + // Check if the instruction sequence is matching now. + checkMatch(condition, + clazz, + method, + codeAttribute, + offset, + branchInstruction); + } + + + public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction) + { + Instruction patternInstruction = patternInstructions[patternInstructionIndex]; + + // Check if the instruction matches the next instruction in the sequence. + boolean condition = + matchingOpcodes(tableSwitchInstruction, patternInstruction) && + matchingBranchOffsets(offset, + tableSwitchInstruction.defaultOffset, + ((TableSwitchInstruction)patternInstruction).defaultOffset) && + matchingArguments(tableSwitchInstruction.lowCase, + ((TableSwitchInstruction)patternInstruction).lowCase) && + matchingArguments(tableSwitchInstruction.highCase, + ((TableSwitchInstruction)patternInstruction).highCase) && + matchingJumpOffsets(offset, + tableSwitchInstruction.jumpOffsets, + ((TableSwitchInstruction)patternInstruction).jumpOffsets); + + // Check if the instruction sequence is matching now. + checkMatch(condition, + clazz, + method, + codeAttribute, + offset, + tableSwitchInstruction); + } + + + public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) + { + Instruction patternInstruction = patternInstructions[patternInstructionIndex]; + + // Check if the instruction matches the next instruction in the sequence. + boolean condition = + matchingOpcodes(lookUpSwitchInstruction, patternInstruction) && + matchingBranchOffsets(offset, + lookUpSwitchInstruction.defaultOffset, + ((LookUpSwitchInstruction)patternInstruction).defaultOffset) && + matchingArguments(lookUpSwitchInstruction.cases, + ((LookUpSwitchInstruction)patternInstruction).cases) && + matchingJumpOffsets(offset, + lookUpSwitchInstruction.jumpOffsets, + ((LookUpSwitchInstruction)patternInstruction).jumpOffsets); + + // Check if the instruction sequence is matching now. + checkMatch(condition, + clazz, + method, + codeAttribute, + offset, + lookUpSwitchInstruction); + } + + + // Implementations for ConstantVisitor. + + public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant) + { + IntegerConstant integerPatternConstant = (IntegerConstant)patternConstant; + + // Compare the integer values. + matchingConstant = integerConstant.getValue() == + integerPatternConstant.getValue(); + } + + + public void visitLongConstant(Clazz clazz, LongConstant longConstant) + { + LongConstant longPatternConstant = (LongConstant)patternConstant; + + // Compare the long values. + matchingConstant = longConstant.getValue() == + longPatternConstant.getValue(); + } + + + public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) + { + FloatConstant floatPatternConstant = (FloatConstant)patternConstant; + + // Compare the float values. + matchingConstant = floatConstant.getValue() == + floatPatternConstant.getValue(); + } + + + public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) + { + DoubleConstant doublePatternConstant = (DoubleConstant)patternConstant; + + // Compare the double values. + matchingConstant = doubleConstant.getValue() == + doublePatternConstant.getValue(); + } + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + StringConstant stringPatternConstant = (StringConstant)patternConstant; + + // Check the UTF-8 constant. + matchingConstant = + matchingConstantIndices(clazz, + stringConstant.u2stringIndex, + stringPatternConstant.u2stringIndex); + } + + + public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) + { + Utf8Constant utf8PatternConstant = (Utf8Constant)patternConstant; + + // Compare the actual strings. + matchingConstant = utf8Constant.getString().equals( + utf8PatternConstant.getString()); + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + RefConstant refPatternConstant = (RefConstant)patternConstant; + + // Check the class and the name and type. + matchingConstant = + matchingConstantIndices(clazz, + refConstant.getClassIndex(), + refPatternConstant.getClassIndex()) && + matchingConstantIndices(clazz, + refConstant.getNameAndTypeIndex(), + refPatternConstant.getNameAndTypeIndex()); + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + ClassConstant classPatternConstant = (ClassConstant)patternConstant; + + // Check the class name. + matchingConstant = + matchingConstantIndices(clazz, + classConstant.u2nameIndex, + classPatternConstant.u2nameIndex); + } + + + public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) + { + NameAndTypeConstant typePatternConstant = (NameAndTypeConstant)patternConstant; + + // Check the name and the descriptor. + matchingConstant = + matchingConstantIndices(clazz, + nameAndTypeConstant.u2nameIndex, + typePatternConstant.u2nameIndex) && + matchingConstantIndices(clazz, + nameAndTypeConstant.u2descriptorIndex, + typePatternConstant.u2descriptorIndex); + } + + + // Small utility methods. + + private boolean matchingOpcodes(Instruction instruction1, + Instruction instruction2) + { + // Check the opcode. + return instruction1.opcode == instruction2.opcode || + instruction1.canonicalOpcode() == instruction2.opcode; + } + + + private boolean matchingArguments(int argument1, + int argument2) + { + int argumentIndex = argument2 - X; + if (argumentIndex < 0) + { + // Check the literal argument. + return argument1 == argument2; + } + else if ((matchedArgumentFlags & (1 << argumentIndex)) == 0) + { + // Store a wildcard argument. + matchedArguments[argumentIndex] = argument1; + matchedArgumentFlags |= 1 << argumentIndex; + + return true; + } + else + { + // Check the previously stored wildcard argument. + return matchedArguments[argumentIndex] == argument1; + } + } + + + private boolean matchingArguments(int[] arguments1, + int[] arguments2) + { + if (arguments1.length != arguments2.length) + { + return false; + } + + for (int index = 0; index < arguments1.length; index++) + { + if (!matchingArguments(arguments1[index], arguments2[index])) + { + return false; + } + } + + return true; + } + + + private boolean matchingConstantIndices(Clazz clazz, + int constantIndex1, + int constantIndex2) + { + if (constantIndex2 >= X) + { + // Check the constant index. + return matchingArguments(constantIndex1, constantIndex2); + } + else if ((matchedConstantFlags & (1L << constantIndex2)) == 0) + { + // Check the actual constant. + matchingConstant = false; + patternConstant = patternConstants[constantIndex2]; + + if (clazz.getTag(constantIndex1) == patternConstant.getTag()) + { + clazz.constantPoolEntryAccept(constantIndex1, this); + + if (matchingConstant) + { + // Store the constant index. + matchedConstantIndices[constantIndex2] = constantIndex1; + matchedConstantFlags |= 1L << constantIndex2; + } + } + + return matchingConstant; + } + else + { + // Check a previously stored constant index. + return matchedConstantIndices[constantIndex2] == constantIndex1; + } + } + + + private boolean matchingBranchOffsets(int offset, + int branchOffset1, + int branchOffset2) + { + int argumentIndex = branchOffset2 - X; + if (argumentIndex < 0) + { + // Check the literal argument. + return branchOffset1 == branchOffset2; + } + else if ((matchedArgumentFlags & (1 << argumentIndex)) == 0) + { + // Store a wildcard argument. + matchedArguments[argumentIndex] = offset + branchOffset1; + matchedArgumentFlags |= 1 << argumentIndex; + + return true; + } + else + { + // Check the previously stored wildcard argument. + return matchedArguments[argumentIndex] == offset + branchOffset1; + } + } + + + private boolean matchingJumpOffsets(int offset, + int[] jumpOffsets1, + int[] jumpOffsets2) + { + if (jumpOffsets1.length != jumpOffsets2.length) + { + return false; + } + + for (int index = 0; index < jumpOffsets1.length; index++) + { + if (!matchingBranchOffsets(offset, + jumpOffsets1[index], + jumpOffsets2[index])) + { + return false; + } + } + + return true; + } + + + private void checkMatch(boolean condition, + Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int offset, + Instruction instruction) + { + if (DEBUG_MORE) + { + System.out.println("InstructionSequenceMatcher: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]: "+patternInstructions[patternInstructionIndex].toString(patternInstructionIndex)+(condition?"\t== ":"\t ")+instruction.toString(offset)); + } + + // Did the instruction match? + if (condition) + { + // Remember the offset of the matching instruction. + matchedInstructionOffsets[patternInstructionIndex] = offset; + + // Try to match the next instruction next time. + patternInstructionIndex++; + + // Did we match all instructions in the sequence? + matching = patternInstructionIndex == patternInstructions.length; + + // Did we match any wildcards along the way? + matchingAnyWildCards = matchedArgumentFlags != 0; + + if (matching) + { + if (DEBUG) + { + System.out.println("InstructionSequenceMatcher: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]"); + for (int index = 0; index < patternInstructionIndex; index++) + { + System.out.println(" "+InstructionFactory.create(codeAttribute.code, matchedInstructionOffsets[index]).toString(matchedInstructionOffsets[index])); + } + } + + // Start matching from the first instruction again next time. + reset(); + } + } + else + { + // The instruction didn't match. + matching = false; + + // Is this a failed second instruction? + boolean retry = patternInstructionIndex == 1; + + // Start matching from the first instruction next time. + reset(); + + // Retry a failed second instruction as a first instruction. + if (retry) + { + instruction.accept(clazz, method, codeAttribute, offset, this); + } + } + } +} diff --git a/src/proguard/classfile/util/InternalTypeEnumeration.java b/src/proguard/classfile/util/InternalTypeEnumeration.java new file mode 100644 index 0000000..76f7e84 --- /dev/null +++ b/src/proguard/classfile/util/InternalTypeEnumeration.java @@ -0,0 +1,204 @@ +/* + * 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.classfile.util; + +import proguard.classfile.ClassConstants; + + +/** + * An <code>InternalTypeEnumeration</code> provides an enumeration of all + * parameter types listed in a given internal method descriptor or signature. + * The signature can also be a class signature. The return type of a method + * descriptor can retrieved separately. + * + * @author Eric Lafortune + */ +public class InternalTypeEnumeration +{ + private String descriptor; + private int firstIndex; + private int lastIndex; + private int index; + + + /** + * Creates a new InternalTypeEnumeration for the given method descriptor. + */ + public InternalTypeEnumeration(String descriptor) + { + this.descriptor = descriptor; + this.firstIndex = descriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN); + this.lastIndex = descriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); + this.index = firstIndex + 1; + + if (lastIndex < 0) + { + lastIndex = descriptor.length(); + } + } + + + /** + * Returns the formal type parameters from the descriptor, assuming it's a + * method descriptor. + */ + public String formalTypeParameters() + { + return descriptor.substring(0, firstIndex); + } + + + /** + * Returns whether the enumeration can provide more types from the method + * descriptor. + */ + public boolean hasMoreTypes() + { + return index < lastIndex; + } + + + /** + * Returns the next type from the method descriptor. + */ + public String nextType() + { + int startIndex = index; + + skipArray(); + + char c = descriptor.charAt(index++); + switch (c) + { + case ClassConstants.INTERNAL_TYPE_CLASS_START: + case ClassConstants.INTERNAL_TYPE_GENERIC_VARIABLE_START: + { + skipClass(); + break; + } + case ClassConstants.INTERNAL_TYPE_GENERIC_START: + { + skipGeneric(); + break; + } + } + + return descriptor.substring(startIndex, index); + } + + + /** + * Returns the return type from the descriptor, assuming it's a method + * descriptor. + */ + public String returnType() + { + return descriptor.substring(lastIndex + 1); + } + + + // Small utility methods. + + private void skipArray() + { + while (descriptor.charAt(index) == ClassConstants.INTERNAL_TYPE_ARRAY) + { + index++; + } + } + + + private void skipClass() + { + while (true) + { + char c = descriptor.charAt(index++); + switch (c) + { + case ClassConstants.INTERNAL_TYPE_GENERIC_START: + skipGeneric(); + break; + + case ClassConstants.INTERNAL_TYPE_CLASS_END: + return; + } + } + } + + + private void skipGeneric() + { + int nestingLevel = 1; + + do + { + char c = descriptor.charAt(index++); + switch (c) + { + case ClassConstants.INTERNAL_TYPE_GENERIC_START: + nestingLevel++; + break; + + case ClassConstants.INTERNAL_TYPE_GENERIC_END: + nestingLevel--; + break; + } + } + while (nestingLevel > 0); + } + + + /** + * A main method for testing the type enumeration. + */ + public static void main(String[] args) + { + try + { + for (int index = 0; index < args.length; index++) + { + String descriptor = args[index]; + + System.out.println("Descriptor ["+descriptor+"]"); + InternalTypeEnumeration enumeration = new InternalTypeEnumeration(descriptor); + + if (enumeration.firstIndex >= 0) + { + System.out.println(" Formal type parameters ["+enumeration.formalTypeParameters()+"]"); + } + + while (enumeration.hasMoreTypes()) + { + System.out.println(" Type ["+enumeration.nextType()+"]"); + } + + if (enumeration.lastIndex < descriptor.length()) + { + System.out.println(" Return type ["+enumeration.returnType()+"]"); + } + } + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } +} diff --git a/src/proguard/classfile/util/MemberFinder.java b/src/proguard/classfile/util/MemberFinder.java new file mode 100644 index 0000000..0fdeec0 --- /dev/null +++ b/src/proguard/classfile/util/MemberFinder.java @@ -0,0 +1,197 @@ +/* + * 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.classfile.util; + +import proguard.classfile.*; +import proguard.classfile.visitor.*; + +/** + * This class provides methods to find class members in a given class or in its + * hierarchy. + * + * @author Eric Lafortune + */ +public class MemberFinder +extends SimplifiedVisitor +implements MemberVisitor +{ + private static class MemberFoundException extends RuntimeException {} + private static final MemberFoundException MEMBER_FOUND = new MemberFoundException(); + + private Clazz clazz; + private Member member; + + + /** + * Finds the field with the given name and descriptor in the given + * class or its hierarchy. + */ + public Field findField(Clazz referencingClass, + Clazz clazz, + String name, + String descriptor) + { + return (Field)findMember(referencingClass, clazz, name, descriptor, true); + } + + + /** + * Finds the method with the given name and descriptor in the given + * class or its hierarchy. + */ + public Method findMethod(Clazz referencingClass, + Clazz clazz, + String name, + String descriptor) + { + return (Method)findMember(referencingClass, clazz, name, descriptor, false); + } + + + /** + * Finds the class member with the given name and descriptor in the given + * class or its hierarchy. + */ + public Member findMember(Clazz referencingClass, + Clazz clazz, + String name, + String descriptor, + boolean isField) + { + // Organize a search in the hierarchy of superclasses and interfaces. + // The class member may be in a different class, if the code was + // compiled with "-target 1.2" or higher (the default in JDK 1.4). + try + { + this.clazz = null; + this.member = null; + clazz.hierarchyAccept(true, true, true, false, isField ? + (ClassVisitor)new NamedFieldVisitor(name, descriptor, + new MemberClassAccessFilter(referencingClass, this)) : + (ClassVisitor)new NamedMethodVisitor(name, descriptor, + new MemberClassAccessFilter(referencingClass, this))); + } + catch (MemberFoundException ex) + { + // We've found the member we were looking for. + } + + return member; + } + + + /** + * Returns the corresponding class of the most recently found class + * member. + */ + public Clazz correspondingClass() + { + return clazz; + } + + + /** + * Returns whether the given method is overridden anywhere down the class + * hierarchy. + */ + public boolean isOverriden(Clazz clazz, + Method method) + { + String name = method.getName(clazz); + String descriptor = method.getDescriptor(clazz); + + // Go looking for the method down the class hierarchy. + try + { + this.clazz = null; + this.member = null; + + clazz.hierarchyAccept(false, false, false, true, + new NamedMethodVisitor(name, descriptor, + new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, this))); + } + catch (MemberFoundException ex) + { + // We've found an overriding method. + return true; + } + + return false; + } + + + /** + * Returns whether the given field is shadowed anywhere down the class + * hierarchy. + */ + public boolean isShadowed(Clazz clazz, + Field field) + { + String name = field.getName(clazz); + String descriptor = field.getDescriptor(clazz); + + // Go looking for the field down the class hierarchy. + try + { + this.clazz = null; + this.member = null; + clazz.hierarchyAccept(false, false, false, true, + new NamedFieldVisitor(name, descriptor, + new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, this))); + } + catch (MemberFoundException ex) + { + // We've found a shadowing field. + return true; + } + + return false; + } + + +// // Implementations for ClassVisitor. +// +// private void visitAnyClass(Clazz clazz) +// { +// if (member == null) +// { +// member = isField ? +// (Member)clazz.findField(name, descriptor) : +// (Member)clazz.findMethod(name, descriptor); +// +// if (member != null) +// { +// this.clazz = clazz; +// } +// } +// } + + + // Implementations for MemberVisitor. + + public void visitAnyMember(Clazz clazz, Member member) + { + this.clazz = clazz; + this.member = member; + + throw MEMBER_FOUND; + } +} diff --git a/src/proguard/classfile/util/MethodLinker.java b/src/proguard/classfile/util/MethodLinker.java new file mode 100644 index 0000000..5f2ea6f --- /dev/null +++ b/src/proguard/classfile/util/MethodLinker.java @@ -0,0 +1,165 @@ +/* + * 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.classfile.util; + +import proguard.classfile.*; +import proguard.classfile.visitor.*; + +import java.util.*; + +/** + * This ClassVisitor links all corresponding non-private methods in the class + * hierarchies of all visited classes. Visited classes are typically all class + * files that are not being subclassed. Chains of links that have been created + * in previous invocations are merged with new chains of links, in order to + * create a consistent set of chains. + * <p> + * As a MemberVisitor, it links all corresponding class members that it visits, + * including fields and private class members. + * <p> + * Class initialization methods and constructors are always ignored. + * + * @author Eric Lafortune + */ +public class MethodLinker +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor +{ + // An object that is reset and reused every time. + // The map: [class member name+' '+descriptor - class member info] + private final Map memberMap = new HashMap(); + + + // Implementations for ClassVisitor. + + public void visitAnyClass(Clazz clazz) + { + // Collect all non-private members in this class hierarchy. + clazz.hierarchyAccept(true, true, true, false, + new AllMethodVisitor( + new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, + this))); + + // Clean up for the next class hierarchy. + memberMap.clear(); + } + + + // Implementations for MemberVisitor. + + public void visitAnyMember(Clazz clazz, Member member) + { + // Get the class member's name and descriptor. + String name = member.getName(clazz); + String descriptor = member.getDescriptor(clazz); + + // Special cases: <clinit> and <init> are always kept unchanged. + // We can ignore them here. + if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) || + name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) + { + return; + } + + // See if we've already come across a method with the same name and + // descriptor. + String key = name + ' ' + descriptor; + Member otherMember = (Member)memberMap.get(key); + + if (otherMember == null) + { + // Get the last method in the chain. + Member thisLastMember = lastMember(member); + + // Store the new class method in the map. + memberMap.put(key, thisLastMember); + } + else + { + // Link both members. + link(member, otherMember); + } + } + + + // Small utility methods. + + /** + * Links the two given class members. + */ + private static void link(Member member1, Member member2) + { + // Get the last methods in the both chains. + Member lastMember1 = lastMember(member1); + Member lastMember2 = lastMember(member2); + + // Check if both link chains aren't already ending in the same element. + if (!lastMember1.equals(lastMember2)) + { + // Merge the two chains, with the library members last. + if (lastMember2 instanceof LibraryMember) + { + lastMember1.setVisitorInfo(lastMember2); + } + else + { + lastMember2.setVisitorInfo(lastMember1); + } + } + } + + + /** + * Finds the last class member in the linked list of related class members. + * @param member the given class member. + * @return the last class member in the linked list. + */ + public static Member lastMember(Member member) + { + Member lastMember = member; + while (lastMember.getVisitorInfo() != null && + lastMember.getVisitorInfo() instanceof Member) + { + lastMember = (Member)lastMember.getVisitorInfo(); + } + + return lastMember; + } + + + /** + * Finds the last visitor accepter in the linked list of visitors. + * @param visitorAccepter the given method. + * @return the last method in the linked list. + */ + public static VisitorAccepter lastVisitorAccepter(VisitorAccepter visitorAccepter) + { + VisitorAccepter lastVisitorAccepter = visitorAccepter; + while (lastVisitorAccepter.getVisitorInfo() != null && + lastVisitorAccepter.getVisitorInfo() instanceof VisitorAccepter) + { + lastVisitorAccepter = (VisitorAccepter)lastVisitorAccepter.getVisitorInfo(); + } + + return lastVisitorAccepter; + } +} diff --git a/src/proguard/classfile/util/SimplifiedVisitor.java b/src/proguard/classfile/util/SimplifiedVisitor.java new file mode 100644 index 0000000..87b7fe4 --- /dev/null +++ b/src/proguard/classfile/util/SimplifiedVisitor.java @@ -0,0 +1,810 @@ +/* + * 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.classfile.util; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.preverification.*; +import proguard.classfile.constant.*; +import proguard.classfile.instruction.*; + +/** + * This abstract utility class allows to implement various visitor interfaces + * with simplified methods. The provided methods delegate to other versions + * with fewer arguments or more general arguments. + * + * @author Eric Lafortune + * @noinspection AbstractClassWithoutAbstractMethods + */ +public abstract class SimplifiedVisitor +{ + // Simplifications for ClassVisitor. + + /** + * Visits any type of class member of the given class. + */ + public void visitAnyClass(Clazz Clazz) + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + + + public void visitProgramClass(ProgramClass programClass) + { + visitAnyClass(programClass); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + visitAnyClass(libraryClass); + } + + + // Simplifications for MemberVisitor. + + /** + * Visits any type of class member of the given class. + */ + public void visitAnyMember(Clazz clazz, Member member) + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + + + /** + * Visits any type of class member of the given program class. + */ + public void visitProgramMember(ProgramClass programClass, ProgramMember programMember) + { + visitAnyMember(programClass, programMember); + } + + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + visitProgramMember(programClass, programField); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + visitProgramMember(programClass, programMethod); + } + + + /** + * Visits any type of class member of the given library class. + */ + public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember) + { + visitAnyMember(libraryClass, libraryMember); + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + visitLibraryMember(libraryClass, libraryField); + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + visitLibraryMember(libraryClass, libraryMethod); + } + + + // Simplifications for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + + + public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant) + { + visitAnyConstant(clazz, integerConstant); + } + + + public void visitLongConstant(Clazz clazz, LongConstant longConstant) + { + visitAnyConstant(clazz, longConstant); + } + + + public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) + { + visitAnyConstant(clazz, floatConstant); + } + + + public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) + { + visitAnyConstant(clazz, doubleConstant); + } + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + visitAnyConstant(clazz, stringConstant); + } + + + public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) + { + visitAnyConstant(clazz, utf8Constant); + } + + + /** + * Visits any type of RefConstant of the given class. + */ + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + visitAnyConstant(clazz, refConstant); + } + + + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) + { + visitAnyRefConstant(clazz, fieldrefConstant); + } + + + /** + * Visits any type of method RefConstant of the given class. + */ + public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant) + { + visitAnyRefConstant(clazz, refConstant); + } + + + public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant) + { + visitAnyMethodrefConstant(clazz, interfaceMethodrefConstant); + } + + + public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + { + visitAnyMethodrefConstant(clazz, methodrefConstant); + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + visitAnyConstant(clazz, classConstant); + } + + + public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) + { + visitAnyConstant(clazz, nameAndTypeConstant); + } + + + // Simplifications for AttributeVisitor. + + /** + * Visit any type of attribute. + */ + public void visitAnyAttribute(Clazz clazz, Attribute attribute) + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + + + public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute) + { + visitAnyAttribute(clazz, unknownAttribute); + } + + + public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute) + { + visitAnyAttribute(clazz, sourceFileAttribute); + } + + + public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute) + { + visitAnyAttribute(clazz, sourceDirAttribute); + } + + + public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) + { + visitAnyAttribute(clazz, innerClassesAttribute); + } + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + visitAnyAttribute(clazz, enclosingMethodAttribute); + } + + + public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) + { + visitAnyAttribute(clazz, deprecatedAttribute); + } + + + /** + * Visits the given DeprecatedAttribute of any type of class member. + */ + public void visitDeprecatedAttribute(Clazz clazz, Member member, DeprecatedAttribute deprecatedAttribute) + { + visitDeprecatedAttribute(clazz, deprecatedAttribute); + } + + + public void visitDeprecatedAttribute(Clazz clazz, Field field, DeprecatedAttribute deprecatedAttribute) + { + visitDeprecatedAttribute(clazz, (Member)field, deprecatedAttribute); + } + + + public void visitDeprecatedAttribute(Clazz clazz, Method method, DeprecatedAttribute deprecatedAttribute) + { + visitDeprecatedAttribute(clazz, (Member)method, deprecatedAttribute); + } + + + public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute) + { + visitAnyAttribute(clazz, syntheticAttribute); + } + + + /** + * Visits the given SyntheticAttribute of any type of class member. + */ + public void visitSyntheticAttribute(Clazz clazz, Member member, SyntheticAttribute syntheticAttribute) + { + visitSyntheticAttribute(clazz, syntheticAttribute); + } + + + public void visitSyntheticAttribute(Clazz clazz, Field field, SyntheticAttribute syntheticAttribute) + { + visitSyntheticAttribute(clazz, (Member)field, syntheticAttribute); + } + + + public void visitSyntheticAttribute(Clazz clazz, Method method, SyntheticAttribute syntheticAttribute) + { + visitSyntheticAttribute(clazz, (Member)method, syntheticAttribute); + } + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + visitAnyAttribute(clazz, signatureAttribute); + } + + + /** + * Visits the given SignatureAttribute of any type of class member. + */ + public void visitSignatureAttribute(Clazz clazz, Member member, SignatureAttribute signatureAttribute) + { + visitSignatureAttribute(clazz, signatureAttribute); + } + + + public void visitSignatureAttribute(Clazz clazz, Field field, SignatureAttribute signatureAttribute) + { + visitSignatureAttribute(clazz, (Member)field, signatureAttribute); + } + + + public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute) + { + visitSignatureAttribute(clazz, (Member)method, signatureAttribute); + } + + + public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute) + { + visitAnyAttribute(clazz, constantValueAttribute); + } + + + public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) + { + visitAnyAttribute(clazz, exceptionsAttribute); + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + visitAnyAttribute(clazz, codeAttribute); + } + + + public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) + { + visitAnyAttribute(clazz, stackMapAttribute); + } + + + public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) + { + visitAnyAttribute(clazz, stackMapTableAttribute); + } + + + public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) + { + visitAnyAttribute(clazz, lineNumberTableAttribute); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + visitAnyAttribute(clazz, localVariableTableAttribute); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + visitAnyAttribute(clazz, localVariableTypeTableAttribute); + } + + + /** + * Visits any type of AnnotationsAttribute of a class. + */ + public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) + { + visitAnyAttribute(clazz, annotationsAttribute); + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + visitAnyAnnotationsAttribute(clazz, runtimeVisibleAnnotationsAttribute); + } + + + /** + * Visits the given RuntimeVisibleAnnotationsAttribute of any type of class member. + */ + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Member member, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + visitRuntimeVisibleAnnotationsAttribute(clazz, runtimeVisibleAnnotationsAttribute); + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + visitRuntimeVisibleAnnotationsAttribute(clazz, (Member)field, runtimeVisibleAnnotationsAttribute); + } + + + public void visitRuntimeVisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute) + { + visitRuntimeVisibleAnnotationsAttribute(clazz, (Member)method, runtimeVisibleAnnotationsAttribute); + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + visitAnyAnnotationsAttribute(clazz, runtimeInvisibleAnnotationsAttribute); + } + + + /** + * Visits the given RuntimeInvisibleAnnotationsAttribute of any type of class member. + */ + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Member member, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + visitRuntimeInvisibleAnnotationsAttribute(clazz, runtimeInvisibleAnnotationsAttribute); + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Field field, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + visitRuntimeInvisibleAnnotationsAttribute(clazz, (Member)field, runtimeInvisibleAnnotationsAttribute); + } + + + public void visitRuntimeInvisibleAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute) + { + visitRuntimeInvisibleAnnotationsAttribute(clazz, (Member)method, runtimeInvisibleAnnotationsAttribute); + } + + + /** + * Visits any type of ParameterAnnotationsAttribute. + */ + public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) + { + visitAnyAttribute(clazz, parameterAnnotationsAttribute); + } + + + public void visitRuntimeVisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleParameterAnnotationsAttribute runtimeVisibleParameterAnnotationsAttribute) + { + visitAnyParameterAnnotationsAttribute(clazz, method, runtimeVisibleParameterAnnotationsAttribute); + } + + + public void visitRuntimeInvisibleParameterAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleParameterAnnotationsAttribute runtimeInvisibleParameterAnnotationsAttribute) + { + visitAnyParameterAnnotationsAttribute(clazz, method, runtimeInvisibleParameterAnnotationsAttribute); + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + visitAnyAttribute(clazz, annotationDefaultAttribute); + } + + + // Simplifications for InstructionVisitor. + + /** + * Visits any type of Instruction. + */ + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + visitAnyInstruction(clazz, method, codeAttribute, offset, simpleInstruction); + } + + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + visitAnyInstruction(clazz, method, codeAttribute, offset, variableInstruction); + } + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + visitAnyInstruction(clazz, method, codeAttribute, offset, constantInstruction); + } + + + public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) + { + visitAnyInstruction(clazz, method, codeAttribute, offset, branchInstruction); + } + + + /** + * Visits either type of SwitchInstruction. + */ + public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) + { + visitAnyInstruction(clazz, method, codeAttribute, offset, switchInstruction); + } + + + public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction) + { + visitAnySwitchInstruction(clazz, method, codeAttribute, offset, tableSwitchInstruction); + } + + + public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) + { + visitAnySwitchInstruction(clazz, method, codeAttribute, offset, lookUpSwitchInstruction); + } + + + // Simplifications for StackMapFrameVisitor. + + /** + * Visits any type of VerificationType. + */ + public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame) + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + + + public void visitSameZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameZeroFrame sameZeroFrame) + { + visitAnyStackMapFrame(clazz, method, codeAttribute, offset, sameZeroFrame); + } + + + public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame) + { + visitAnyStackMapFrame(clazz, method, codeAttribute, offset, sameOneFrame); + } + + + public void visitLessZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LessZeroFrame lessZeroFrame) + { + visitAnyStackMapFrame(clazz, method, codeAttribute, offset, lessZeroFrame); + } + + + public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame) + { + visitAnyStackMapFrame(clazz, method, codeAttribute, offset, moreZeroFrame); + } + + + public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame) + { + visitAnyStackMapFrame(clazz, method, codeAttribute, offset, fullFrame); + } + + + // Simplifications for VerificationTypeVisitor. + + /** + * Visits any type of VerificationType. + */ + public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + + + public void visitIntegerType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, IntegerType integerType) + { + visitAnyVerificationType(clazz, method, codeAttribute, offset, integerType); + } + + + public void visitFloatType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FloatType floatType) + { + visitAnyVerificationType(clazz, method, codeAttribute, offset, floatType); + } + + + public void visitLongType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LongType longType) + { + visitAnyVerificationType(clazz, method, codeAttribute, offset, longType); + } + + + public void visitDoubleType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, DoubleType doubleType) + { + visitAnyVerificationType(clazz, method, codeAttribute, offset, doubleType); + } + + + public void visitTopType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TopType topType) + { + visitAnyVerificationType(clazz, method, codeAttribute, offset, topType); + } + + + public void visitObjectType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ObjectType objectType) + { + visitAnyVerificationType(clazz, method, codeAttribute, offset, objectType); + } + + + public void visitNullType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, NullType nullType) + { + visitAnyVerificationType(clazz, method, codeAttribute, offset, nullType); + } + + + public void visitUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedType uninitializedType) + { + visitAnyVerificationType(clazz, method, codeAttribute, offset, uninitializedType); + } + + + public void visitUninitializedThisType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, UninitializedThisType uninitializedThisType) + { + visitAnyVerificationType(clazz, method, codeAttribute, offset, uninitializedThisType); + } + + + public void visitStackIntegerType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, IntegerType integerType) + { + visitIntegerType(clazz, method, codeAttribute, offset, integerType); + } + + + public void visitStackFloatType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, FloatType floatType) + { + visitFloatType(clazz, method, codeAttribute, offset, floatType); + } + + + public void visitStackLongType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, LongType longType) + { + visitLongType(clazz, method, codeAttribute, offset, longType); + } + + + public void visitStackDoubleType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, DoubleType doubleType) + { + visitDoubleType(clazz, method, codeAttribute, offset, doubleType); + } + + + public void visitStackTopType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, TopType topType) + { + visitTopType(clazz, method, codeAttribute, offset, topType); + } + + + public void visitStackObjectType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, ObjectType objectType) + { + visitObjectType(clazz, method, codeAttribute, offset, objectType); + } + + + public void visitStackNullType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, NullType nullType) + { + visitNullType(clazz, method, codeAttribute, offset, nullType); + } + + + public void visitStackUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, UninitializedType uninitializedType) + { + visitUninitializedType(clazz, method, codeAttribute, offset, uninitializedType); + } + + + public void visitStackUninitializedThisType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, UninitializedThisType uninitializedThisType) + { + visitUninitializedThisType(clazz, method, codeAttribute, offset, uninitializedThisType); + } + + + + public void visitVariablesIntegerType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, IntegerType integerType) + { + visitIntegerType(clazz, method, codeAttribute, offset, integerType); + } + + + public void visitVariablesFloatType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, FloatType floatType) + { + visitFloatType(clazz, method, codeAttribute, offset, floatType); + } + + + public void visitVariablesLongType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, LongType longType) + { + visitLongType(clazz, method, codeAttribute, offset, longType); + } + + + public void visitVariablesDoubleType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, DoubleType doubleType) + { + visitDoubleType(clazz, method, codeAttribute, offset, doubleType); + } + + + public void visitVariablesTopType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, TopType topType) + { + visitTopType(clazz, method, codeAttribute, offset, topType); + } + + + public void visitVariablesObjectType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, ObjectType objectType) + { + visitObjectType(clazz, method, codeAttribute, offset, objectType); + } + + + public void visitVariablesNullType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, NullType nullType) + { + visitNullType(clazz, method, codeAttribute, offset, nullType); + } + + + public void visitVariablesUninitializedType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, UninitializedType uninitializedType) + { + visitUninitializedType(clazz, method, codeAttribute, offset, uninitializedType); + } + + + public void visitVariablesUninitializedThisType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, int index, UninitializedThisType uninitializedThisType) + { + visitUninitializedThisType(clazz, method, codeAttribute, offset, uninitializedThisType); + } + + + // Simplifications for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + + + /** + * Visits the given Annotation of any type of class member. + */ + public void visitAnnotation(Clazz clazz, Member member, Annotation annotation) + { + visitAnnotation(clazz, annotation); + } + + + public void visitAnnotation(Clazz clazz, Field field, Annotation annotation) + { + visitAnnotation(clazz, (Member)field, annotation); + } + + + public void visitAnnotation(Clazz clazz, Method method, Annotation annotation) + { + visitAnnotation(clazz, (Member)method, annotation); + } + + + public void visitAnnotation(Clazz clazz, Method method, int parameterIndex, Annotation annotation) + { + visitAnnotation(clazz, method, annotation); + } + + + // Simplifications for ElementValueVisitor. + + public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue) + { + throw new UnsupportedOperationException("Method must be overridden in ["+this.getClass().getName()+"] if ever called"); + } + + + public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) + { + visitAnyElementValue(clazz, annotation, constantElementValue); + } + + + public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) + { + visitAnyElementValue(clazz, annotation, enumConstantElementValue); + } + + + public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) + { + visitAnyElementValue(clazz, annotation, classElementValue); + } + + + public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) + { + visitAnyElementValue(clazz, annotation, annotationElementValue); + } + + + public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) + { + visitAnyElementValue(clazz, annotation, arrayElementValue); + } +} diff --git a/src/proguard/classfile/util/StringReferenceInitializer.java b/src/proguard/classfile/util/StringReferenceInitializer.java new file mode 100644 index 0000000..3884a04 --- /dev/null +++ b/src/proguard/classfile/util/StringReferenceInitializer.java @@ -0,0 +1,89 @@ +/* + * 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.classfile.util; + +import proguard.classfile.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; + +/** + * This ConstantVisitor initializes any class references of all string constants + * it visits. More specifically, it fills out the references of string constant + * pool entries that happen to refer to a class in the program class pool or in + * the library class pool. + * + * @author Eric Lafortune + */ +public class StringReferenceInitializer +extends SimplifiedVisitor +implements ConstantVisitor +{ + private final ClassPool programClassPool; + private final ClassPool libraryClassPool; + + + /** + * Creates a new StringReferenceInitializer. + */ + public StringReferenceInitializer(ClassPool programClassPool, + ClassPool libraryClassPool) + { + this.programClassPool = programClassPool; + this.libraryClassPool = libraryClassPool; + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + if (stringConstant.referencedClass == null) + { + // See if we can find the referenced class. + stringConstant.referencedClass = + findClass(ClassUtil.internalClassName(stringConstant.getString(clazz))); + } + } + + + // Small utility methods. + + /** + * 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 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); + } + + return clazz; + } +}
\ No newline at end of file diff --git a/src/proguard/classfile/util/StringSharer.java b/src/proguard/classfile/util/StringSharer.java new file mode 100644 index 0000000..56de7c5 --- /dev/null +++ b/src/proguard/classfile/util/StringSharer.java @@ -0,0 +1,155 @@ +/* + * 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.classfile.util; + +import proguard.classfile.*; +import proguard.classfile.attribute.Attribute; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This ClassVisitor shares strings in the class files that it visits. + * + * @author Eric Lafortune + */ +public class StringSharer +extends SimplifiedVisitor +implements ClassVisitor, + ConstantVisitor, + AttributeVisitor +{ + // A fields acting as an argument for the visitor methods. + private String name; + private String type; + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Replace name strings in the constant pool by shared strings. + programClass.constantPoolEntriesAccept(this); + + // Replace attribute name strings in the constant pool by internalized + // strings. + programClass.attributesAccept(this); + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + // Replace the super class name string by the shared name string. + Clazz superClass = libraryClass.superClass; + if (superClass != null) + { + libraryClass.superClassName = superClass.getName(); + } + } + + + // Implementations for ConstantVisitor. + + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitAnyStringConstant(Clazz clazz, StringConstant stringConstant) + { + Member referencedMember = stringConstant.referencedMember; + if (referencedMember != null) + { + Clazz referencedClass = stringConstant.referencedClass; + + // Put the actual class member's name in the class pool. + name = referencedMember.getName(referencedClass); + clazz.constantPoolEntryAccept(stringConstant.u2stringIndex, this); + } + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + Member referencedMember = refConstant.referencedMember; + if (referencedMember != null) + { + Clazz referencedClass = refConstant.referencedClass; + + // Put the actual class member's name and type strings in the class + // pool. + name = referencedMember.getName(referencedClass); + type = referencedMember.getDescriptor(referencedClass); + clazz.constantPoolEntryAccept(refConstant.u2nameAndTypeIndex, this); + } + } + + + public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) + { + if (name != null) + { + // Put the actual class member's name and type strings in the class + // pool. + clazz.constantPoolEntryAccept(nameAndTypeConstant.u2nameIndex, this); + name = type; + clazz.constantPoolEntryAccept(nameAndTypeConstant.u2descriptorIndex, this); + } + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + Clazz referencedClass = classConstant.referencedClass; + if (referencedClass != null) + { + // Put the actual class's name string in the class pool. + name = referencedClass.getName(); + clazz.constantPoolEntryAccept(classConstant.u2nameIndex, this); + } + } + + + public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) + { + // Do we have a new string to put into this constant? + if (name != null) + { + // Replace the string, if it's actually the same. + if (name.equals(utf8Constant.getString())) + { + utf8Constant.setString(name); + } + + name = null; + } + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) + { + // Put the internalized attribute's name string in the class pool. + name = attribute.getAttributeName(clazz).intern(); + clazz.constantPoolEntryAccept(attribute.u2attributeNameIndex, this); + } +} diff --git a/src/proguard/classfile/util/WarningPrinter.java b/src/proguard/classfile/util/WarningPrinter.java new file mode 100644 index 0000000..87d8978 --- /dev/null +++ b/src/proguard/classfile/util/WarningPrinter.java @@ -0,0 +1,136 @@ +/* + * 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.classfile.util; + +import proguard.util.*; + +import java.io.PrintStream; +import java.util.List; + +/** + * This class prints out and counts warnings. + * + * @author Eric Lafortune + */ +public class WarningPrinter +{ + private final PrintStream printStream; + private final StringMatcher classFilter; + private int warningCount; + + + /** + * Creates a new WarningPrinter that prints to the System.err print stream. + */ + public WarningPrinter() + { + this(System.err); + } + + + /** + * Creates a new WarningPrinter that prints to the given print stream. + */ + public WarningPrinter(PrintStream printStream) + { + this.printStream = printStream; + this.classFilter = null; + } + + + /** + * Creates a new WarningPrinter that prints to the given print stream, + * except if the names of any involved classes matches the given filter. + */ + public WarningPrinter(PrintStream printStream, List classFilter) + { + this.printStream = printStream; + this.classFilter = classFilter == null ? null : + new ListParser(new ClassNameParser()).parse(classFilter); + } + + + /** + * Prints out the given warning and increments the warning count, if + * the given class name passes the class name filter. + */ + public void print(String className, String warning) + { + if (accepts(className)) + { + print(warning); + } + } + + + /** + * Returns whether the given class name passes the class name filter. + */ + public boolean accepts(String className) + { + return classFilter == null || + !classFilter.matches(className); + } + + + /** + * Prints out the given warning and increments the warning count, if + * the given class names pass the class name filter. + */ + public void print(String className1, String className2, String warning) + { + if (accepts(className1, className2)) + { + print(warning); + } + } + + + /** + * Returns whether the given class names pass the class name filter. + */ + public boolean accepts(String className1, String className2) + { + return classFilter == null || + !(classFilter.matches(className1) || + classFilter.matches(className2)); + } + + + /** + * Prints out the given warning and increments the warning count. + */ + private void print(String warning) + { + printStream.println(warning); + + warningCount++; + } + + + /** + * Returns the number of warnings printed so far. + */ + public int getWarningCount() + { + return warningCount; + } +} diff --git a/src/proguard/classfile/util/package.html b/src/proguard/classfile/util/package.html new file mode 100644 index 0000000..b1b881e --- /dev/null +++ b/src/proguard/classfile/util/package.html @@ -0,0 +1,3 @@ +<body> +This package contains utility classes for processing class files. +</body> |