Home | History | Annotate | Download | only in editor
      1 /*
      2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
      3  *             of Java bytecode.
      4  *
      5  * Copyright (c) 2002-2009 Eric Lafortune (eric (at) graphics.cornell.edu)
      6  *
      7  * This program is free software; you can redistribute it and/or modify it
      8  * under the terms of the GNU General Public License as published by the Free
      9  * Software Foundation; either version 2 of the License, or (at your option)
     10  * any later version.
     11  *
     12  * This program is distributed in the hope that it will be useful, but WITHOUT
     13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
     15  * more details.
     16  *
     17  * You should have received a copy of the GNU General Public License along
     18  * with this program; if not, write to the Free Software Foundation, Inc.,
     19  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
     20  */
     21 package proguard.classfile.editor;
     22 
     23 import proguard.classfile.*;
     24 import proguard.classfile.attribute.*;
     25 import proguard.classfile.attribute.annotation.*;
     26 import proguard.classfile.attribute.annotation.visitor.*;
     27 import proguard.classfile.attribute.visitor.*;
     28 import proguard.classfile.constant.*;
     29 import proguard.classfile.constant.visitor.ConstantVisitor;
     30 import proguard.classfile.util.*;
     31 import proguard.classfile.visitor.*;
     32 
     33 /**
     34  * This ClassVisitor fixes references of constant pool entries, fields,
     35  * methods, and attributes to classes whose names have changed. Descriptors
     36  * of member references are not updated yet.
     37  *
     38  * @see MemberReferenceFixer
     39  * @author Eric Lafortune
     40  */
     41 public class ClassReferenceFixer
     42 extends      SimplifiedVisitor
     43 implements   ClassVisitor,
     44              ConstantVisitor,
     45              MemberVisitor,
     46              AttributeVisitor,
     47              InnerClassesInfoVisitor,
     48              LocalVariableInfoVisitor,
     49              LocalVariableTypeInfoVisitor,
     50              AnnotationVisitor,
     51              ElementValueVisitor
     52 {
     53     private final boolean ensureUniqueMemberNames;
     54 
     55 
     56     /**
     57      * Creates a new ClassReferenceFixer.
     58      * @param ensureUniqueMemberNames specifies whether class members whose
     59      *                                descriptor changes should get new, unique
     60      *                                names, in order to avoid naming conflicts
     61      *                                with similar methods.
     62      */
     63     public ClassReferenceFixer(boolean ensureUniqueMemberNames)
     64     {
     65         this.ensureUniqueMemberNames = ensureUniqueMemberNames;
     66     }
     67 
     68 
     69     // Implementations for ClassVisitor.
     70 
     71     public void visitProgramClass(ProgramClass programClass)
     72     {
     73         // Fix the constant pool.
     74         programClass.constantPoolEntriesAccept(this);
     75 
     76         // Fix class members.
     77         programClass.fieldsAccept(this);
     78         programClass.methodsAccept(this);
     79 
     80         // Fix the attributes.
     81         programClass.attributesAccept(this);
     82     }
     83 
     84 
     85     public void visitLibraryClass(LibraryClass libraryClass)
     86     {
     87         // Fix class members.
     88         libraryClass.fieldsAccept(this);
     89         libraryClass.methodsAccept(this);
     90     }
     91 
     92 
     93     // Implementations for MemberVisitor.
     94 
     95     public void visitProgramField(ProgramClass programClass, ProgramField programField)
     96     {
     97         // Has the descriptor changed?
     98         String descriptor    = programField.getDescriptor(programClass);
     99         String newDescriptor = newDescriptor(descriptor,
    100                                              programField.referencedClass);
    101 
    102         if (!descriptor.equals(newDescriptor))
    103         {
    104             ConstantPoolEditor constantPoolEditor =
    105                 new ConstantPoolEditor(programClass);
    106 
    107             // Update the descriptor.
    108             programField.u2descriptorIndex =
    109                 constantPoolEditor.addUtf8Constant(newDescriptor);
    110 
    111             // Update the name, if requested.
    112             if (ensureUniqueMemberNames)
    113             {
    114                 String name    = programField.getName(programClass);
    115                 String newName = newUniqueMemberName(name, descriptor);
    116                 programField.u2nameIndex =
    117                     constantPoolEditor.addUtf8Constant(newName);
    118             }
    119         }
    120 
    121         // Fix the attributes.
    122         programField.attributesAccept(programClass, this);
    123     }
    124 
    125 
    126     public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
    127     {
    128         // Has the descriptor changed?
    129         String descriptor    = programMethod.getDescriptor(programClass);
    130         String newDescriptor = newDescriptor(descriptor,
    131                                              programMethod.referencedClasses);
    132 
    133         if (!descriptor.equals(newDescriptor))
    134         {
    135             ConstantPoolEditor constantPoolEditor =
    136                 new ConstantPoolEditor(programClass);
    137 
    138             // Update the descriptor.
    139             programMethod.u2descriptorIndex =
    140                 constantPoolEditor.addUtf8Constant(newDescriptor);
    141 
    142             // Update the name, if requested.
    143             if (ensureUniqueMemberNames)
    144             {
    145                 String name    = programMethod.getName(programClass);
    146                 String newName = newUniqueMemberName(name, descriptor);
    147                 programMethod.u2nameIndex =
    148                     constantPoolEditor.addUtf8Constant(newName);
    149             }
    150         }
    151 
    152         // Fix the attributes.
    153         programMethod.attributesAccept(programClass, this);
    154     }
    155 
    156 
    157     public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
    158     {
    159         // Has the descriptor changed?
    160         String descriptor    = libraryField.getDescriptor(libraryClass);
    161         String newDescriptor = newDescriptor(descriptor,
    162                                              libraryField.referencedClass);
    163 
    164         // Update the descriptor.
    165         libraryField.descriptor = newDescriptor;
    166     }
    167 
    168 
    169     public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
    170     {
    171         // Has the descriptor changed?
    172         String descriptor    = libraryMethod.getDescriptor(libraryClass);
    173         String newDescriptor = newDescriptor(descriptor,
    174                                              libraryMethod.referencedClasses);
    175 
    176         // Update the descriptor.
    177         libraryMethod.descriptor = newDescriptor;
    178     }
    179 
    180 
    181     // Implementations for ConstantVisitor.
    182 
    183     public void visitAnyConstant(Clazz clazz, Constant constant) {}
    184 
    185 
    186     public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
    187     {
    188         // Does the string refer to a class, due to a Class.forName construct?
    189         Clazz  referencedClass  = stringConstant.referencedClass;
    190         Member referencedMember = stringConstant.referencedMember;
    191         if (referencedClass  != null &&
    192             referencedMember == null)
    193         {
    194             // Reconstruct the new class name.
    195             String externalClassName    = stringConstant.getString(clazz);
    196             String internalClassName    = ClassUtil.internalClassName(externalClassName);
    197             String newInternalClassName = newClassName(internalClassName,
    198                                                        referencedClass);
    199 
    200             // Update the String entry if required.
    201             if (!newInternalClassName.equals(internalClassName))
    202             {
    203                 String newExternalClassName = ClassUtil.externalClassName(newInternalClassName);
    204 
    205                 // Refer to a new Utf8 entry.
    206                 stringConstant.u2stringIndex =
    207                     new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newExternalClassName);
    208             }
    209         }
    210     }
    211 
    212 
    213     public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
    214     {
    215         // Do we know the referenced class?
    216         Clazz referencedClass = classConstant.referencedClass;
    217         if (referencedClass != null)
    218         {
    219             // Has the class name changed?
    220             String className    = classConstant.getName(clazz);
    221             String newClassName = newClassName(className, referencedClass);
    222             if (!className.equals(newClassName))
    223             {
    224                 // Refer to a new Utf8 entry.
    225                 classConstant.u2nameIndex =
    226                     new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newClassName);
    227             }
    228         }
    229     }
    230 
    231     // Implementations for AttributeVisitor.
    232 
    233     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
    234 
    235 
    236     public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
    237     {
    238         // Fix the inner class names.
    239         innerClassesAttribute.innerClassEntriesAccept(clazz, this);
    240     }
    241 
    242 
    243     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
    244     {
    245         // Fix the attributes.
    246         codeAttribute.attributesAccept(clazz, method, this);
    247     }
    248 
    249 
    250     public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
    251     {
    252         // Fix the types of the local variables.
    253         localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
    254     }
    255 
    256 
    257     public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
    258     {
    259         // Fix the signatures of the local variables.
    260         localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
    261     }
    262 
    263 
    264     public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
    265     {
    266         // Compute the new signature.
    267         String signature    = clazz.getString(signatureAttribute.u2signatureIndex);
    268         String newSignature = newDescriptor(signature,
    269                                             signatureAttribute.referencedClasses);
    270 
    271         if (!signature.equals(newSignature))
    272         {
    273             signatureAttribute.u2signatureIndex =
    274                 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature);
    275         }
    276     }
    277 
    278 
    279     public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
    280     {
    281         // Fix the annotations.
    282         annotationsAttribute.annotationsAccept(clazz, this);
    283     }
    284 
    285 
    286     public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
    287     {
    288         // Fix the annotations.
    289         parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
    290     }
    291 
    292 
    293     public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
    294     {
    295         // Fix the annotation.
    296         annotationDefaultAttribute.defaultValueAccept(clazz, this);
    297     }
    298 
    299 
    300     // Implementations for InnerClassesInfoVisitor.
    301 
    302     public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo)
    303     {
    304         // Fix the inner class name.
    305         int innerClassIndex = innerClassesInfo.u2innerClassIndex;
    306         int innerNameIndex  = innerClassesInfo.u2innerNameIndex;
    307         if (innerClassIndex != 0 &&
    308             innerNameIndex  != 0)
    309         {
    310             String newInnerName = clazz.getClassName(innerClassIndex);
    311             int index = newInnerName.lastIndexOf(ClassConstants.INTERNAL_INNER_CLASS_SEPARATOR);
    312             if (index >= 0)
    313             {
    314                 innerClassesInfo.u2innerNameIndex =
    315                     new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newInnerName.substring(index + 1));
    316             }
    317         }
    318     }
    319 
    320 
    321     // Implementations for LocalVariableInfoVisitor.
    322 
    323     public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
    324     {
    325         // Has the descriptor changed?
    326         String descriptor    = clazz.getString(localVariableInfo.u2descriptorIndex);
    327         String newDescriptor = newDescriptor(descriptor,
    328                                              localVariableInfo.referencedClass);
    329 
    330         if (!descriptor.equals(newDescriptor))
    331         {
    332             // Refer to a new Utf8 entry.
    333             localVariableInfo.u2descriptorIndex =
    334                 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newDescriptor);
    335         }
    336     }
    337 
    338     // Implementations for LocalVariableTypeInfoVisitor.
    339 
    340     public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
    341     {
    342         // Has the signature changed?
    343         String signature    = clazz.getString(localVariableTypeInfo.u2signatureIndex);
    344         String newSignature = newDescriptor(signature,
    345                                             localVariableTypeInfo.referencedClasses);
    346 
    347         if (!signature.equals(newSignature))
    348         {
    349             localVariableTypeInfo.u2signatureIndex =
    350                 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature);
    351         }
    352     }
    353 
    354     // Implementations for AnnotationVisitor.
    355 
    356     public void visitAnnotation(Clazz clazz, Annotation annotation)
    357     {
    358         // Compute the new type name.
    359         String typeName    = clazz.getString(annotation.u2typeIndex);
    360         String newTypeName = newDescriptor(typeName,
    361                                            annotation.referencedClasses);
    362 
    363         if (!typeName.equals(newTypeName))
    364         {
    365             // Refer to a new Utf8 entry.
    366             annotation.u2typeIndex =
    367                 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newTypeName);
    368         }
    369 
    370         // Fix the element values.
    371         annotation.elementValuesAccept(clazz, this);
    372     }
    373 
    374 
    375     // Implementations for ElementValueVisitor.
    376 
    377     public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
    378     {
    379     }
    380 
    381 
    382     public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
    383     {
    384         // Compute the new type name.
    385         String typeName    = clazz.getString(enumConstantElementValue.u2typeNameIndex);
    386         String newTypeName = newDescriptor(typeName,
    387                                            enumConstantElementValue.referencedClasses);
    388 
    389         if (!typeName.equals(newTypeName))
    390         {
    391             // Refer to a new Utf8 entry.
    392             enumConstantElementValue.u2typeNameIndex =
    393                 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newTypeName);
    394         }
    395     }
    396 
    397 
    398     public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
    399     {
    400         // Compute the new class name.
    401         String className    = clazz.getString(classElementValue.u2classInfoIndex);
    402         String newClassName = newDescriptor(className,
    403                                             classElementValue.referencedClasses);
    404 
    405         if (!className.equals(newClassName))
    406         {
    407             // Refer to a new Utf8 entry.
    408             classElementValue.u2classInfoIndex =
    409                 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newClassName);
    410         }
    411     }
    412 
    413 
    414     public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
    415     {
    416         // Fix the annotation.
    417         annotationElementValue.annotationAccept(clazz, this);
    418     }
    419 
    420 
    421     public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
    422     {
    423         // Fix the element values.
    424         arrayElementValue.elementValuesAccept(clazz, annotation, this);
    425     }
    426 
    427 
    428     // Small utility methods.
    429 
    430     private static String newDescriptor(String descriptor,
    431                                         Clazz  referencedClass)
    432     {
    433         // If there is no referenced class, the descriptor won't change.
    434         if (referencedClass == null)
    435         {
    436             return descriptor;
    437         }
    438 
    439         // Unravel and reconstruct the class element of the descriptor.
    440         DescriptorClassEnumeration descriptorClassEnumeration =
    441             new DescriptorClassEnumeration(descriptor);
    442 
    443         StringBuffer newDescriptorBuffer = new StringBuffer(descriptor.length());
    444         newDescriptorBuffer.append(descriptorClassEnumeration.nextFluff());
    445 
    446         // Only if the descriptor contains a class name (e.g. with an array of
    447         // primitive types), the descriptor can change.
    448         if (descriptorClassEnumeration.hasMoreClassNames())
    449         {
    450             String className = descriptorClassEnumeration.nextClassName();
    451             String fluff     = descriptorClassEnumeration.nextFluff();
    452 
    453             String newClassName = newClassName(className,
    454                                                referencedClass);
    455 
    456             newDescriptorBuffer.append(newClassName);
    457             newDescriptorBuffer.append(fluff);
    458         }
    459 
    460         return newDescriptorBuffer.toString();
    461     }
    462 
    463 
    464     private static String newDescriptor(String  descriptor,
    465                                         Clazz[] referencedClasses)
    466     {
    467         // If there are no referenced classes, the descriptor won't change.
    468         if (referencedClasses == null ||
    469             referencedClasses.length == 0)
    470         {
    471             return descriptor;
    472         }
    473 
    474         // Unravel and reconstruct the class elements of the descriptor.
    475         DescriptorClassEnumeration descriptorClassEnumeration =
    476             new DescriptorClassEnumeration(descriptor);
    477 
    478         StringBuffer newDescriptorBuffer = new StringBuffer(descriptor.length());
    479         newDescriptorBuffer.append(descriptorClassEnumeration.nextFluff());
    480 
    481         int index = 0;
    482         while (descriptorClassEnumeration.hasMoreClassNames())
    483         {
    484             String  className        = descriptorClassEnumeration.nextClassName();
    485             boolean isInnerClassName = descriptorClassEnumeration.isInnerClassName();
    486             String  fluff            = descriptorClassEnumeration.nextFluff();
    487 
    488             String newClassName = newClassName(className,
    489                                                referencedClasses[index++]);
    490 
    491             // Strip the outer class name again, if it's an inner class.
    492             if (isInnerClassName)
    493             {
    494                 newClassName =
    495                     newClassName.substring(newClassName.lastIndexOf(ClassConstants.INTERNAL_INNER_CLASS_SEPARATOR)+1);
    496             }
    497 
    498             newDescriptorBuffer.append(newClassName);
    499             newDescriptorBuffer.append(fluff);
    500         }
    501 
    502         return newDescriptorBuffer.toString();
    503     }
    504 
    505 
    506     /**
    507      * Returns a unique class member name, based on the given name and descriptor.
    508      */
    509     private String newUniqueMemberName(String name, String descriptor)
    510     {
    511         return name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ?
    512             ClassConstants.INTERNAL_METHOD_NAME_INIT :
    513             name + ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode()));
    514     }
    515 
    516 
    517     /**
    518      * Returns the new class name based on the given class name and the new
    519      * name of the given referenced class. Class names of array types
    520      * are handled properly.
    521      */
    522     private static String newClassName(String className,
    523                                        Clazz  referencedClass)
    524     {
    525         // If there is no referenced class, the class name won't change.
    526         if (referencedClass == null)
    527         {
    528             return className;
    529         }
    530 
    531         // Reconstruct the class name.
    532         String newClassName = referencedClass.getName();
    533 
    534         // Is it an array type?
    535         if (className.charAt(0) == ClassConstants.INTERNAL_TYPE_ARRAY)
    536         {
    537             // Add the array prefixes and suffix "[L...;".
    538             newClassName =
    539                  className.substring(0, className.indexOf(ClassConstants.INTERNAL_TYPE_CLASS_START)+1) +
    540                  newClassName +
    541                  ClassConstants.INTERNAL_TYPE_CLASS_END;
    542         }
    543 
    544         return newClassName;
    545     }
    546 }
    547