Home | History | Annotate | Download | only in editor
      1 /*
      2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
      3  *             of Java bytecode.
      4  *
      5  * Copyright (c) 2002-2014 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.AttributeVisitor;
     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 constant pool field and method references to fields
     35  * and methods whose names or descriptors have changed.
     36  *
     37  * @author Eric Lafortune
     38  */
     39 public class MemberReferenceFixer
     40 extends      SimplifiedVisitor
     41 implements   ClassVisitor,
     42              ConstantVisitor,
     43              MemberVisitor,
     44              AttributeVisitor,
     45              AnnotationVisitor,
     46              ElementValueVisitor
     47 {
     48     private static final boolean DEBUG = false;
     49 
     50 
     51     private final StackSizeUpdater stackSizeUpdater = new StackSizeUpdater();
     52 
     53     // Parameter for the visitor methods.
     54     private int constantIndex;
     55 
     56     // Return values for the visitor methods.
     57     private boolean isInterfaceMethod;
     58     private boolean stackSizesMayHaveChanged;
     59 
     60 
     61     // Implementations for ClassVisitor.
     62 
     63     public void visitProgramClass(ProgramClass programClass)
     64     {
     65         stackSizesMayHaveChanged = false;
     66 
     67         // Fix the constant pool entries.
     68         for (int index = 1; index < programClass.u2constantPoolCount; index++)
     69         {
     70             Constant constant = programClass.constantPool[index];
     71             if (constant != null)
     72             {
     73                 // Fix the entry, replacing it entirely if needed.
     74                 this.constantIndex = index;
     75 
     76                 constant.accept(programClass, this);
     77             }
     78         }
     79 
     80         // Fix the class members.
     81         programClass.fieldsAccept(this);
     82         programClass.methodsAccept(this);
     83 
     84         // Fix the attributes.
     85         programClass.attributesAccept(this);
     86     }
     87 
     88 
     89     // Implementations for ConstantVisitor.
     90 
     91     public void visitAnyConstant(Clazz clazz, Constant constant) {}
     92 
     93 
     94     public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
     95     {
     96         // Does the string refer to a class member, due to a
     97         // Class.get[Declared]{Field,Method} construct?
     98         Member referencedMember = stringConstant.referencedMember;
     99         if (referencedMember != null)
    100         {
    101             Clazz referencedClass = stringConstant.referencedClass;
    102 
    103             // Does it have a new name?
    104             String newName = referencedMember.getName(referencedClass);
    105 
    106             if (!stringConstant.getString(clazz).equals(newName))
    107             {
    108                 if (DEBUG)
    109                 {
    110                     debug(clazz, stringConstant, referencedClass, referencedMember);
    111                 }
    112 
    113                 // Update the name.
    114                 stringConstant.u2stringIndex =
    115                     new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newName);
    116             }
    117         }
    118     }
    119 
    120 
    121     public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)
    122     {
    123         // Do we know the referenced field?
    124         Member referencedMember = fieldrefConstant.referencedMember;
    125         if (referencedMember != null)
    126         {
    127             Clazz referencedClass = fieldrefConstant.referencedClass;
    128 
    129             // Does it have a new name or type?
    130             String newName = referencedMember.getName(referencedClass);
    131             String newType = referencedMember.getDescriptor(referencedClass);
    132 
    133             if (!fieldrefConstant.getName(clazz).equals(newName) ||
    134                 !fieldrefConstant.getType(clazz).equals(newType))
    135             {
    136                 if (DEBUG)
    137                 {
    138                     debug(clazz, fieldrefConstant, referencedClass, referencedMember);
    139                 }
    140 
    141                 // Update the name and type index.
    142                 fieldrefConstant.u2nameAndTypeIndex =
    143                     new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName, newType);
    144             }
    145         }
    146     }
    147 
    148 
    149     public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant)
    150     {
    151         // Do we know the referenced interface method?
    152         Member referencedMember = interfaceMethodrefConstant.referencedMember;
    153         if (referencedMember != null)
    154         {
    155             Clazz referencedClass = interfaceMethodrefConstant.referencedClass;
    156 
    157             // Does it have a new name or type?
    158             String newName = referencedMember.getName(referencedClass);
    159             String newType = referencedMember.getDescriptor(referencedClass);
    160 
    161             if (!interfaceMethodrefConstant.getName(clazz).equals(newName) ||
    162                 !interfaceMethodrefConstant.getType(clazz).equals(newType))
    163             {
    164                 if (DEBUG)
    165                 {
    166                     debug(clazz, interfaceMethodrefConstant, referencedClass, referencedMember);
    167                 }
    168 
    169                 // Update the name and type index.
    170                 interfaceMethodrefConstant.u2nameAndTypeIndex =
    171                     new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName, newType);
    172 
    173                 // Remember that the stack sizes of the methods in this class
    174                 // may have changed.
    175                 stackSizesMayHaveChanged = true;
    176             }
    177 
    178             // Check if this is an interface method.
    179             isInterfaceMethod = true;
    180             clazz.constantPoolEntryAccept(interfaceMethodrefConstant.u2classIndex, this);
    181 
    182             // Has the method become a non-interface method?
    183             if (!isInterfaceMethod)
    184             {
    185                 if (DEBUG)
    186                 {
    187                     System.out.println("MemberReferenceFixer:");
    188                     System.out.println("  Class file     = "+clazz.getName());
    189                     System.out.println("  Ref class      = "+referencedClass.getName());
    190                     System.out.println("  Ref method     = "+interfaceMethodrefConstant.getName(clazz)+interfaceMethodrefConstant.getType(clazz));
    191                     System.out.println("    -> ordinary method");
    192                 }
    193 
    194                 // Replace the interface method reference by a method reference.
    195                 ((ProgramClass)clazz).constantPool[this.constantIndex] =
    196                     new MethodrefConstant(interfaceMethodrefConstant.u2classIndex,
    197                                           interfaceMethodrefConstant.u2nameAndTypeIndex,
    198                                           referencedClass,
    199                                           referencedMember);
    200             }
    201         }
    202     }
    203 
    204 
    205     public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)
    206     {
    207         // Do we know the referenced method?
    208         Member referencedMember = methodrefConstant.referencedMember;
    209         if (referencedMember != null)
    210         {
    211             Clazz referencedClass = methodrefConstant.referencedClass;
    212 
    213             // Does it have a new name or type?
    214             String newName = referencedMember.getName(referencedClass);
    215             String newType = referencedMember.getDescriptor(referencedClass);
    216 
    217             if (!methodrefConstant.getName(clazz).equals(newName) ||
    218                 !methodrefConstant.getType(clazz).equals(newType))
    219             {
    220                 if (DEBUG)
    221                 {
    222                     debug(clazz, methodrefConstant, referencedClass, referencedMember);
    223                 }
    224 
    225                 // Update the name and type index.
    226                 methodrefConstant.u2nameAndTypeIndex =
    227                     new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName, newType);
    228 
    229                 // Remember that the stack sizes of the methods in this class
    230                 // may have changed.
    231                 stackSizesMayHaveChanged = true;
    232             }
    233 
    234             // Check if this is an interface method.
    235             isInterfaceMethod = false;
    236             clazz.constantPoolEntryAccept(methodrefConstant.u2classIndex, this);
    237 
    238             // Has the method become an interface method?
    239             if (isInterfaceMethod)
    240             {
    241                 if (DEBUG)
    242                 {
    243                     System.out.println("MemberReferenceFixer:");
    244                     System.out.println("  Class file     = "+clazz.getName());
    245                     System.out.println("  Ref class      = "+referencedClass.getName());
    246                     System.out.println("  Ref method     = "+methodrefConstant.getName(clazz)+methodrefConstant.getType(clazz));
    247                     System.out.println("    -> interface method");
    248                 }
    249 
    250                 // Replace the method reference by an interface method reference.
    251                 ((ProgramClass)clazz).constantPool[this.constantIndex] =
    252                     new InterfaceMethodrefConstant(methodrefConstant.u2classIndex,
    253                                                    methodrefConstant.u2nameAndTypeIndex,
    254                                                    referencedClass,
    255                                                    referencedMember);
    256             }
    257         }
    258     }
    259 
    260 
    261     public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
    262     {
    263         // Check if this class entry is an array type.
    264         if (ClassUtil.isInternalArrayType(classConstant.getName(clazz)))
    265         {
    266             isInterfaceMethod = false;
    267         }
    268         else
    269         {
    270             // Check if this class entry refers to an interface class.
    271             Clazz referencedClass = classConstant.referencedClass;
    272             if (referencedClass != null)
    273             {
    274                 isInterfaceMethod = (referencedClass.getAccessFlags() & ClassConstants.ACC_INTERFACE) != 0;
    275             }
    276         }
    277     }
    278 
    279 
    280     // Implementations for MemberVisitor.
    281 
    282     public void visitProgramMember(ProgramClass programClass, ProgramMember programMember)
    283     {
    284         // Fix the attributes.
    285         programMember.attributesAccept(programClass, this);
    286     }
    287 
    288 
    289     // Implementations for AttributeVisitor.
    290 
    291     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
    292 
    293 
    294     public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
    295     {
    296         Member referencedMember = enclosingMethodAttribute.referencedMethod;
    297         if (referencedMember != null)
    298         {
    299             Clazz referencedClass = enclosingMethodAttribute.referencedClass;
    300 
    301             // Does it have a new name or type?
    302             String newName = referencedMember.getName(referencedClass);
    303             String newType = referencedMember.getDescriptor(referencedClass);
    304 
    305             if (!enclosingMethodAttribute.getName(clazz).equals(newName) ||
    306                 !enclosingMethodAttribute.getType(clazz).equals(newType))
    307             {
    308                 // Update the name and type index.
    309                 enclosingMethodAttribute.u2nameAndTypeIndex =
    310                     new ConstantPoolEditor((ProgramClass)clazz).addNameAndTypeConstant(newName,
    311                                                                                        newType);
    312             }
    313         }
    314     }
    315 
    316 
    317     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
    318     {
    319         // Recompute the maximum stack size if necessary.
    320         if (stackSizesMayHaveChanged)
    321         {
    322             stackSizeUpdater.visitCodeAttribute(clazz, method, codeAttribute);
    323         }
    324 
    325         // Fix the nested attributes.
    326         codeAttribute.attributesAccept(clazz, method, this);
    327     }
    328 
    329 
    330     public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
    331     {
    332         // Fix the annotations.
    333         annotationsAttribute.annotationsAccept(clazz, this);
    334     }
    335 
    336 
    337     public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
    338     {
    339         // Fix the annotations.
    340         parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
    341     }
    342 
    343 
    344     public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
    345     {
    346         // Fix the annotation.
    347         annotationDefaultAttribute.defaultValueAccept(clazz, this);
    348     }
    349 
    350 
    351     // Implementations for AnnotationVisitor.
    352 
    353     public void visitAnnotation(Clazz clazz, Annotation annotation)
    354     {
    355         // Fix the element values.
    356         annotation.elementValuesAccept(clazz, this);
    357     }
    358 
    359 
    360     // Implementations for ElementValueVisitor.
    361 
    362     public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
    363     {
    364         fixElementValue(clazz, annotation, constantElementValue);
    365     }
    366 
    367 
    368     public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
    369     {
    370         fixElementValue(clazz, annotation, enumConstantElementValue);
    371     }
    372 
    373 
    374     public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
    375     {
    376         fixElementValue(clazz, annotation, classElementValue);
    377     }
    378 
    379 
    380     public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
    381     {
    382         fixElementValue(clazz, annotation, annotationElementValue);
    383 
    384         // Fix the annotation.
    385         annotationElementValue.annotationAccept(clazz, this);
    386     }
    387 
    388 
    389     public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
    390     {
    391         fixElementValue(clazz, annotation, arrayElementValue);
    392 
    393         // Fix the element values.
    394         arrayElementValue.elementValuesAccept(clazz, annotation, this);
    395     }
    396 
    397 
    398     // Small utility methods.
    399 
    400     /**
    401      * Fixes the method reference of the element value, if any.
    402      */
    403     private void fixElementValue(Clazz        clazz,
    404                                  Annotation   annotation,
    405                                  ElementValue elementValue)
    406     {
    407         // Do we know the referenced method?
    408         Member referencedMember = elementValue.referencedMethod;
    409         if (referencedMember != null)
    410         {
    411             // Does it have a new name or type?
    412             String methodName    = elementValue.getMethodName(clazz);
    413             String newMethodName = referencedMember.getName(elementValue.referencedClass);
    414 
    415             if (!methodName.equals(newMethodName))
    416             {
    417                 // Update the element name index.
    418                 elementValue.u2elementNameIndex =
    419                     new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newMethodName);
    420             }
    421         }
    422     }
    423 
    424 
    425     private void debug(Clazz          clazz,
    426                        StringConstant stringConstant,
    427                        Clazz          referencedClass,
    428                        Member         referencedMember)
    429     {
    430         System.out.println("MemberReferenceFixer:");
    431         System.out.println("  ["+clazz.getName()+"]: String ["+
    432                            stringConstant.getString(clazz)+"] -> ["+
    433                            referencedClass.getName()+"."+referencedMember.getName(referencedClass)+" "+referencedMember.getDescriptor(referencedClass)+"]");
    434     }
    435 
    436 
    437     private void debug(Clazz       clazz,
    438                        RefConstant refConstant,
    439                        Clazz       referencedClass,
    440                        Member      referencedMember)
    441     {
    442         System.out.println("MemberReferenceFixer:");
    443         System.out.println("  ["+clazz.getName()+"]: ["+
    444                            refConstant.getClassName(clazz)+"."+refConstant.getName(clazz)+" "+refConstant.getType(clazz)+"] -> ["+
    445                            referencedClass.getName()+"."+referencedMember.getName(referencedClass)+" "+referencedMember.getDescriptor(referencedClass)+"]");
    446     }
    447 }
    448