diff options
Diffstat (limited to 'src/proguard/optimize/peephole/TargetClassChanger.java')
-rw-r--r-- | src/proguard/optimize/peephole/TargetClassChanger.java | 439 |
1 files changed, 439 insertions, 0 deletions
diff --git a/src/proguard/optimize/peephole/TargetClassChanger.java b/src/proguard/optimize/peephole/TargetClassChanger.java new file mode 100644 index 0000000..22fd83d --- /dev/null +++ b/src/proguard/optimize/peephole/TargetClassChanger.java @@ -0,0 +1,439 @@ +/* + * 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.optimize.peephole; + +import proguard.classfile.*; +import proguard.classfile.editor.*; +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.util.*; +import proguard.classfile.visitor.*; + +/** + * This ClassVisitor replaces references to classes and class members if the + * classes have targets that are intended to replace them. + * + * @see VerticalClassMerger + * @see ClassReferenceFixer + * @see MemberReferenceFixer + * @author Eric Lafortune + */ +public class TargetClassChanger +extends SimplifiedVisitor +implements ClassVisitor, + ConstantVisitor, + MemberVisitor, + AttributeVisitor, + LocalVariableInfoVisitor, + LocalVariableTypeInfoVisitor, + AnnotationVisitor, + ElementValueVisitor +{ + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + Clazz superClass = null; + Clazz[] interfaceClasses = null; + + // Change the references of the constant pool. + programClass.constantPoolEntriesAccept(this); + + // Change the references of the class members. + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + + // Change the references of the attributes. + programClass.attributesAccept(this); + + // Is the class itself being retargeted? + Clazz targetClass = ClassMerger.getTargetClass(programClass); + if (targetClass != null) + { + // Restore the class name. We have to add a new class entry + // to avoid an existing entry with the same name being reused. The + // names have to be fixed later, based on their referenced classes. + programClass.u2thisClass = + addNewClassConstant(programClass, + programClass.getName(), + programClass); + + // This class will loose all its subclasses. + programClass.subClasses = null; + } + + // Remove interface classes that are pointing to this class. + int newInterfacesCount = 0; + for (int index = 0; index < programClass.u2interfacesCount; index++) + { + Clazz interfaceClass = programClass.getInterface(index); + if (!programClass.equals(interfaceClass)) + { + programClass.u2interfaces[newInterfacesCount++] = + programClass.u2interfaces[index]; + } + } + programClass.u2interfacesCount = newInterfacesCount; + + // Update the subclasses of the superclass and interfaces of the + // target class. + ConstantVisitor subclassAdder = + new ReferencedClassVisitor( + new SubclassFilter(programClass, + new SubclassAdder(programClass))); + + programClass.superClassConstantAccept(subclassAdder); + programClass.interfaceConstantsAccept(subclassAdder); + + // TODO: Maybe restore private method references. + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + // Change the references of the class members. + libraryClass.fieldsAccept(this); + libraryClass.methodsAccept(this); + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + // Change the referenced class. + programField.referencedClass = + updateReferencedClass(programField.referencedClass); + + // Change the references of the attributes. + programField.attributesAccept(programClass, this); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + // Change the referenced classes. + updateReferencedClasses(programMethod.referencedClasses); + + // Change the references of the attributes. + programMethod.attributesAccept(programClass, this); + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + // Change the referenced class. + libraryField.referencedClass = + updateReferencedClass(libraryField.referencedClass); + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + // Change the referenced classes. + updateReferencedClasses(libraryMethod.referencedClasses); + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + // Does the string refer to a class, due to a Class.forName construct? + Clazz referencedClass = stringConstant.referencedClass; + Clazz newReferencedClass = updateReferencedClass(referencedClass); + if (referencedClass != newReferencedClass) + { + // Change the referenced class. + stringConstant.referencedClass = newReferencedClass; + + // Change the referenced class member, if applicable. + stringConstant.referencedMember = + updateReferencedMember(stringConstant.referencedMember, + stringConstant.getString(clazz), + null, + newReferencedClass); + } + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + Clazz referencedClass = refConstant.referencedClass; + Clazz newReferencedClass = updateReferencedClass(referencedClass); + if (referencedClass != newReferencedClass) + { + // Change the referenced class. + refConstant.referencedClass = newReferencedClass; + + // Change the referenced class member. + refConstant.referencedMember = + updateReferencedMember(refConstant.referencedMember, + refConstant.getName(clazz), + refConstant.getType(clazz), + newReferencedClass); + } + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Change the referenced class. + classConstant.referencedClass = + updateReferencedClass(classConstant.referencedClass); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Change the references of the attributes. + codeAttribute.attributesAccept(clazz, method, this); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + // Change the references of the local variables. + localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + // Change the references of the local variables. + localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) + { + // Change the referenced classes. + updateReferencedClasses(signatureAttribute.referencedClasses); + } + + + public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) + { + // Change the references of the annotations. + annotationsAttribute.annotationsAccept(clazz, this); + } + + + public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) + { + // Change the references of the annotations. + parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + // Change the references of the annotation. + annotationDefaultAttribute.defaultValueAccept(clazz, this); + } + + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + // Change the referenced class. + localVariableInfo.referencedClass = + updateReferencedClass(localVariableInfo.referencedClass); + } + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + // Change the referenced classes. + updateReferencedClasses(localVariableTypeInfo.referencedClasses); + } + + // Implementations for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + // Change the referenced classes. + updateReferencedClasses(annotation.referencedClasses); + + // Change the references of the element values. + annotation.elementValuesAccept(clazz, this); + } + + + // Implementations for ElementValueVisitor. + + public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue) + { + Clazz referencedClass = elementValue.referencedClass; + Clazz newReferencedClass = updateReferencedClass(referencedClass); + if (referencedClass != newReferencedClass) + { + // Change the referenced annotation class. + elementValue.referencedClass = newReferencedClass; + + // Change the referenced method. + elementValue.referencedMethod = + (Method)updateReferencedMember(elementValue.referencedMethod, + elementValue.getMethodName(clazz), + null, + newReferencedClass); + } + } + + + public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) + { + // Change the referenced annotation class and method. + visitAnyElementValue(clazz, annotation, constantElementValue); + } + + + public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) + { + // Change the referenced annotation class and method. + visitAnyElementValue(clazz, annotation, enumConstantElementValue); + + // Change the referenced classes. + updateReferencedClasses(enumConstantElementValue.referencedClasses); + } + + + public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) + { + // Change the referenced annotation class and method. + visitAnyElementValue(clazz, annotation, classElementValue); + + // Change the referenced classes. + updateReferencedClasses(classElementValue.referencedClasses); + } + + + public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) + { + // Change the referenced annotation class and method. + visitAnyElementValue(clazz, annotation, annotationElementValue); + + // Change the references of the annotation. + annotationElementValue.annotationAccept(clazz, this); + } + + + public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) + { + // Change the referenced annotation class and method. + visitAnyElementValue(clazz, annotation, arrayElementValue); + + // Change the references of the element values. + arrayElementValue.elementValuesAccept(clazz, annotation, this); + } + + + // Small utility methods. + + /** + * Updates the retargeted classes in the given array of classes. + */ + private void updateReferencedClasses(Clazz[] referencedClasses) + { + if (referencedClasses == null) + { + return; + } + + for (int index = 0; index < referencedClasses.length; index++) + { + referencedClasses[index] = + updateReferencedClass(referencedClasses[index]); + } + } + + + /** + * Returns the retargeted class of the given class. + */ + private Clazz updateReferencedClass(Clazz referencedClass) + { + if (referencedClass == null) + { + return null; + } + + Clazz targetClazz = ClassMerger.getTargetClass(referencedClass); + return targetClazz != null ? + targetClazz : + referencedClass; + } + + + /** + * Returns the retargeted class member of the given class member. + */ + private Member updateReferencedMember(Member referencedMember, + String name, + String type, + Clazz newReferencedClass) + { + if (referencedMember == null) + { + return null; + } + + return referencedMember instanceof Field ? + (Member)newReferencedClass.findField(name, type) : + (Member)newReferencedClass.findMethod(name, type); + } + + + /** + * Explicitly adds a new class constant for the given class in the given + * program class. + */ + private int addNewClassConstant(ProgramClass programClass, + String className, + Clazz referencedClass) + { + ConstantPoolEditor constantPoolEditor = + new ConstantPoolEditor(programClass); + + int nameIndex = + constantPoolEditor.addUtf8Constant(className); + + int classConstantIndex = + constantPoolEditor.addConstant(new ClassConstant(nameIndex, + referencedClass)); + return classConstantIndex; + } +}
\ No newline at end of file |