diff options
Diffstat (limited to 'src/proguard/classfile/editor/MemberReferenceFixer.java')
-rw-r--r-- | src/proguard/classfile/editor/MemberReferenceFixer.java | 456 |
1 files changed, 456 insertions, 0 deletions
diff --git a/src/proguard/classfile/editor/MemberReferenceFixer.java b/src/proguard/classfile/editor/MemberReferenceFixer.java new file mode 100644 index 0000000..4bd8af5 --- /dev/null +++ b/src/proguard/classfile/editor/MemberReferenceFixer.java @@ -0,0 +1,456 @@ +/* + * 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.editor; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.annotation.visitor.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; + +/** + * This ClassVisitor fixes constant pool field and method references to fields + * and methods whose names or descriptors have changed. + * + * @author Eric Lafortune + */ +public class MemberReferenceFixer +extends SimplifiedVisitor +implements ClassVisitor, + ConstantVisitor, + MemberVisitor, + AttributeVisitor, + AnnotationVisitor, + ElementValueVisitor +{ + private static final boolean DEBUG = false; + + + private final StackSizeUpdater stackSizeUpdater = new StackSizeUpdater(); + + // Parameter for the visitor methods. + private int constantIndex; + + // Return values for the visitor methods. + private boolean isInterfaceMethod; + private boolean stackSizesMayHaveChanged; + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + stackSizesMayHaveChanged = false; + + // Fix the constant pool entries. + for (int index = 1; index < programClass.u2constantPoolCount; index++) + { + Constant constant = programClass.constantPool[index]; + if (constant != null) + { + // Fix the entry, replacing it entirely if needed. + this.constantIndex = index; + + constant.accept(programClass, this); + } + } + + // Fix the class members. + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + + // Fix the attributes. + programClass.attributesAccept(this); + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + // Does the string refer to a class member, due to a + // Class.get[Declared]{Field,Method} construct? + Member referencedMember = stringConstant.referencedMember; + if (referencedMember != null) + { + Clazz referencedClass = stringConstant.referencedClass; + + // Does it have a new name? + String newName = referencedMember.getName(referencedClass); + + if (!stringConstant.getString(clazz).equals(newName)) + { + if (DEBUG) + { + debug(clazz, stringConstant, referencedClass, referencedMember); + } + + // Update the name. + stringConstant.u2stringIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newName); + } + } + } + + + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) + { + // Do we know the referenced field? + Member referencedMember = fieldrefConstant.referencedMember; + if (referencedMember != null) + { + Clazz referencedClass = fieldrefConstant.referencedClass; + + // Does it have a new name or type? + String newName = referencedMember.getName(referencedClass); + String newType = referencedMember.getDescriptor(referencedClass); + + if (!fieldrefConstant.getName(clazz).equals(newName) || + !fieldrefConstant.getType(clazz).equals(newType)) + { + if (DEBUG) + { + debug(clazz, fieldrefConstant, referencedClass, referencedMember); + } + + // Update the name and type index. + fieldrefConstant.u2nameAndTypeIndex = + new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName, newType); + } + } + } + + + public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant) + { + // Do we know the referenced interface method? + Member referencedMember = interfaceMethodrefConstant.referencedMember; + if (referencedMember != null) + { + Clazz referencedClass = interfaceMethodrefConstant.referencedClass; + + // Does it have a new name or type? + String newName = referencedMember.getName(referencedClass); + String newType = referencedMember.getDescriptor(referencedClass); + + if (!interfaceMethodrefConstant.getName(clazz).equals(newName) || + !interfaceMethodrefConstant.getType(clazz).equals(newType)) + { + if (DEBUG) + { + debug(clazz, interfaceMethodrefConstant, referencedClass, referencedMember); + } + + // Update the name and type index. + interfaceMethodrefConstant.u2nameAndTypeIndex = + new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName, newType); + + // Remember that the stack sizes of the methods in this class + // may have changed. + stackSizesMayHaveChanged = true; + } + + // Check if this is an interface method. + isInterfaceMethod = true; + clazz.constantPoolEntryAccept(interfaceMethodrefConstant.u2classIndex, this); + + // Has the method become a non-interface method? + if (!isInterfaceMethod) + { + if (DEBUG) + { + System.out.println("MemberReferenceFixer:"); + System.out.println(" Class file = "+clazz.getName()); + System.out.println(" Ref class = "+referencedClass.getName()); + System.out.println(" Ref method = "+interfaceMethodrefConstant.getName(clazz)+interfaceMethodrefConstant.getType(clazz)); + System.out.println(" -> ordinary method"); + } + + // Replace the interface method reference by a method reference. + ((ProgramClass)clazz).constantPool[this.constantIndex] = + new MethodrefConstant(interfaceMethodrefConstant.u2classIndex, + interfaceMethodrefConstant.u2nameAndTypeIndex, + referencedClass, + referencedMember); + } + } + } + + + public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + { + // Do we know the referenced method? + Member referencedMember = methodrefConstant.referencedMember; + if (referencedMember != null) + { + Clazz referencedClass = methodrefConstant.referencedClass; + + // Does it have a new name or type? + String newName = referencedMember.getName(referencedClass); + String newType = referencedMember.getDescriptor(referencedClass); + + if (!methodrefConstant.getName(clazz).equals(newName) || + !methodrefConstant.getType(clazz).equals(newType)) + { + if (DEBUG) + { + debug(clazz, methodrefConstant, referencedClass, referencedMember); + } + + // Update the name and type index. + methodrefConstant.u2nameAndTypeIndex = + new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName, newType); + + // Remember that the stack sizes of the methods in this class + // may have changed. + stackSizesMayHaveChanged = true; + } + + // Check if this is an interface method. + isInterfaceMethod = false; + clazz.constantPoolEntryAccept(methodrefConstant.u2classIndex, this); + + // Has the method become an interface method? + if (isInterfaceMethod) + { + if (DEBUG) + { + System.out.println("MemberReferenceFixer:"); + System.out.println(" Class file = "+clazz.getName()); + System.out.println(" Ref class = "+referencedClass.getName()); + System.out.println(" Ref method = "+methodrefConstant.getName(clazz)+methodrefConstant.getType(clazz)); + System.out.println(" -> interface method"); + } + + // Replace the method reference by an interface method reference. + ((ProgramClass)clazz).constantPool[this.constantIndex] = + new InterfaceMethodrefConstant(methodrefConstant.u2classIndex, + methodrefConstant.u2nameAndTypeIndex, + referencedClass, + referencedMember); + } + } + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Check if this class entry is an array type. + if (ClassUtil.isInternalArrayType(classConstant.getName(clazz))) + { + isInterfaceMethod = false; + } + else + { + // Check if this class entry refers to an interface class. + Clazz referencedClass = classConstant.referencedClass; + if (referencedClass != null) + { + isInterfaceMethod = (referencedClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE) != 0; + } + } + } + + + // Implementations for MemberVisitor. + + public void visitProgramMember(ProgramClass programClass, ProgramMember programMember) + { + // Fix the attributes. + programMember.attributesAccept(programClass, this); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) + { + Member referencedMember = enclosingMethodAttribute.referencedMethod; + if (referencedMember != null) + { + Clazz referencedClass = enclosingMethodAttribute.referencedClass; + + // Does it have a new class? + if (!enclosingMethodAttribute.getClassName(clazz).equals(referencedClass.getName())) + { + // Update the class index. + enclosingMethodAttribute.u2classIndex = + new ConstantPoolEditor((ProgramClass)clazz).addClassConstant(referencedClass); + } + + // Does it have a new name or type? + if (!enclosingMethodAttribute.getName(clazz).equals(referencedMember.getName(referencedClass)) || + !enclosingMethodAttribute.getType(clazz).equals(referencedMember.getDescriptor(referencedClass))) + { + // Update the name and type index. + enclosingMethodAttribute.u2nameAndTypeIndex = + new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(referencedMember.getName(referencedClass), + referencedMember.getDescriptor(referencedClass)); + } + } + } + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Recompute the maximum stack size if necessary. + if (stackSizesMayHaveChanged) + { + stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute); + } + + // Fix the nested attributes. + codeAttribute.attributesAccept(clazz, method, this); + } + + + public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) + { + // Fix the annotations. + annotationsAttribute.annotationsAccept(clazz, this); + } + + + public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) + { + // Fix the annotations. + parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); + } + + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) + { + // Fix the annotation. + annotationDefaultAttribute.defaultValueAccept(clazz, this); + } + + + // Implementations for AnnotationVisitor. + + public void visitAnnotation(Clazz clazz, Annotation annotation) + { + // Fix the element values. + annotation.elementValuesAccept(clazz, this); + } + + + // Implementations for ElementValueVisitor. + + public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) + { + fixElementValue(clazz, annotation, constantElementValue); + } + + + public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) + { + fixElementValue(clazz, annotation, enumConstantElementValue); + } + + + public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) + { + fixElementValue(clazz, annotation, classElementValue); + } + + + public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) + { + fixElementValue(clazz, annotation, annotationElementValue); + + // Fix the annotation. + annotationElementValue.annotationAccept(clazz, this); + } + + + public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) + { + fixElementValue(clazz, annotation, arrayElementValue); + + // Fix the element values. + arrayElementValue.elementValuesAccept(clazz, annotation, this); + } + + + // Small utility methods. + + /** + * Fixes the method reference of the element value, if any. + */ + private void fixElementValue(Clazz clazz, + Annotation annotation, + ElementValue elementValue) + { + // Do we know the referenced method? + Member referencedMember = elementValue.referencedMethod; + if (referencedMember != null) + { + // Does it have a new name or type? + String methodName = elementValue.getMethodName(clazz); + String newMethodName = referencedMember.getName(elementValue.referencedClass); + + if (!methodName.equals(newMethodName)) + { + // Update the element name index. + elementValue.u2elementNameIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newMethodName); + } + } + } + + + private void debug(Clazz clazz, + StringConstant stringConstant, + Clazz referencedClass, + Member referencedMember) + { + System.out.println("MemberReferenceFixer:"); + System.out.println(" Class file = "+clazz.getName()); + System.out.println(" Ref class = "+referencedClass.getName()); + System.out.println(" Ref member name = "+stringConstant.getString(clazz)); + System.out.println(" -> "+referencedMember.getName(referencedClass)); + } + + + private void debug(Clazz clazz, + RefConstant refConstant, + Clazz referencedClass, + Member referencedMember) + { + System.out.println("MemberReferenceFixer:"); + System.out.println(" Class file = "+clazz.getName()); + System.out.println(" Ref class = "+referencedClass.getName()); + System.out.println(" Ref member name = "+refConstant.getName(clazz)); + System.out.println(" -> "+referencedMember.getName(referencedClass)); + System.out.println(" Ref descriptor = "+refConstant.getType(clazz)); + System.out.println(" -> "+referencedMember.getDescriptor(referencedClass)); + } +} |