Home | History | Annotate | Download | only in peephole
      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.optimize.peephole;
     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.editor.*;
     31 import proguard.classfile.util.SimplifiedVisitor;
     32 import proguard.classfile.visitor.*;
     33 
     34 /**
     35  * This ClassVisitor replaces references to classes and class members if the
     36  * classes have targets that are intended to replace them.
     37  *
     38  * @see VerticalClassMerger
     39  * @see ClassReferenceFixer
     40  * @see MemberReferenceFixer
     41  * @author Eric Lafortune
     42  */
     43 public class TargetClassChanger
     44 extends      SimplifiedVisitor
     45 implements   ClassVisitor,
     46              ConstantVisitor,
     47              MemberVisitor,
     48              AttributeVisitor,
     49              LocalVariableInfoVisitor,
     50              LocalVariableTypeInfoVisitor,
     51              AnnotationVisitor,
     52              ElementValueVisitor
     53 {
     54     private static final boolean DEBUG = false;
     55 
     56 
     57     // Implementations for ClassVisitor.
     58 
     59     public void visitProgramClass(ProgramClass programClass)
     60     {
     61         // We're only making changes locally in the class.
     62         // Not all other classes may have been retargeted yet.
     63 
     64         // Change the references of the constant pool.
     65         programClass.constantPoolEntriesAccept(this);
     66 
     67         // Change the references of the class members.
     68         programClass.fieldsAccept(this);
     69         programClass.methodsAccept(this);
     70 
     71         // Change the references of the attributes.
     72         programClass.attributesAccept(this);
     73 
     74         // Remove duplicate interfaces and interface classes that have ended
     75         // up pointing to the class itself.
     76         boolean[] delete = null;
     77         for (int index = 0; index < programClass.u2interfacesCount; index++)
     78         {
     79             Clazz interfaceClass = programClass.getInterface(index);
     80             if (interfaceClass != null &&
     81                 (programClass.equals(interfaceClass) ||
     82                  containsInterfaceClass(programClass,
     83                                         index,
     84                                         interfaceClass)))
     85             {
     86                 // Lazily create the array.
     87                 if (delete == null)
     88                 {
     89                     delete = new boolean[programClass.u2interfacesCount];
     90                 }
     91 
     92                 delete[index] = true;
     93             }
     94         }
     95 
     96         if (delete != null)
     97         {
     98             new InterfaceDeleter(delete).visitProgramClass(programClass);
     99         }
    100 
    101         // Is the class being retargeted?
    102         Clazz targetClass = ClassMerger.getTargetClass(programClass);
    103         if (targetClass != null)
    104         {
    105             // We're not changing anything special in the superclass and
    106             // interface hierarchy of the retargeted class. The shrinking
    107             // step will remove the class for us.
    108 
    109             // Restore the class name. We have to add a new class entry
    110             // to avoid an existing entry with the same name being reused. The
    111             // names have to be fixed later, based on their referenced classes.
    112             programClass.u2thisClass =
    113                 addNewClassConstant(programClass,
    114                                     programClass.getName(),
    115                                     programClass);
    116 
    117             // This class will no longer have any subclasses, because their
    118             // subclasses and interfaces will be retargeted.
    119             programClass.subClasses = null;
    120         }
    121         else
    122         {
    123             // This class has become the subclass of its possibly new
    124             // superclass and of any new interfaces.
    125             ConstantVisitor subclassAdder =
    126                 new ReferencedClassVisitor(
    127                 new SubclassFilter(programClass,
    128                 new SubclassAdder(programClass)));
    129 
    130             programClass.superClassConstantAccept(subclassAdder);
    131             programClass.interfaceConstantsAccept(subclassAdder);
    132 
    133             // TODO: Maybe restore private method references.
    134         }
    135     }
    136 
    137 
    138     public void visitLibraryClass(LibraryClass libraryClass)
    139     {
    140         // Change the references of the class members.
    141         libraryClass.fieldsAccept(this);
    142         libraryClass.methodsAccept(this);
    143     }
    144 
    145 
    146     // Implementations for MemberVisitor.
    147 
    148     public void visitProgramField(ProgramClass programClass, ProgramField programField)
    149     {
    150         // Change the referenced class.
    151         programField.referencedClass =
    152             updateReferencedClass(programField.referencedClass);
    153 
    154         // Change the references of the attributes.
    155         programField.attributesAccept(programClass, this);
    156     }
    157 
    158 
    159     public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
    160     {
    161         // Change the referenced classes.
    162         updateReferencedClasses(programMethod.referencedClasses);
    163 
    164         // Change the references of the attributes.
    165         programMethod.attributesAccept(programClass, this);
    166     }
    167 
    168 
    169     public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField)
    170     {
    171         // Change the referenced class.
    172         libraryField.referencedClass =
    173             updateReferencedClass(libraryField.referencedClass);
    174     }
    175 
    176 
    177     public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod)
    178     {
    179         // Change the referenced classes.
    180         updateReferencedClasses(libraryMethod.referencedClasses);
    181     }
    182 
    183 
    184     // Implementations for ConstantVisitor.
    185 
    186     public void visitAnyConstant(Clazz clazz, Constant constant) {}
    187 
    188 
    189     public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
    190     {
    191         // Does the string refer to a class, due to a Class.forName construct?
    192         Clazz referencedClass    = stringConstant.referencedClass;
    193         Clazz newReferencedClass = updateReferencedClass(referencedClass);
    194         if (referencedClass != newReferencedClass)
    195         {
    196             // Change the referenced class.
    197             stringConstant.referencedClass = newReferencedClass;
    198 
    199             // Change the referenced class member, if applicable.
    200             stringConstant.referencedMember =
    201                 updateReferencedMember(stringConstant.referencedMember,
    202                                        stringConstant.getString(clazz),
    203                                        null,
    204                                        newReferencedClass);
    205         }
    206     }
    207 
    208 
    209     public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
    210     {
    211         Clazz referencedClass    = refConstant.referencedClass;
    212         Clazz newReferencedClass = updateReferencedClass(referencedClass);
    213         if (referencedClass != newReferencedClass)
    214         {
    215             if (DEBUG)
    216             {
    217                 System.out.println("TargetClassChanger:");
    218                 System.out.println("  ["+clazz.getName()+"] changing reference from ["+refConstant.referencedClass+"."+refConstant.referencedMember.getName(refConstant.referencedClass)+refConstant.referencedMember.getDescriptor(refConstant.referencedClass)+"]");
    219             }
    220 
    221             // Change the referenced class.
    222             refConstant.referencedClass  = newReferencedClass;
    223 
    224             // Change the referenced class member.
    225             refConstant.referencedMember =
    226                 updateReferencedMember(refConstant.referencedMember,
    227                                        refConstant.getName(clazz),
    228                                        refConstant.getType(clazz),
    229                                        newReferencedClass);
    230 
    231             if (DEBUG)
    232             {
    233                 System.out.println("  ["+clazz.getName()+"]                    to   ["+refConstant.referencedClass+"."+refConstant.referencedMember.getName(refConstant.referencedClass)+refConstant.referencedMember.getDescriptor(refConstant.referencedClass)+"]");
    234             }
    235         }
    236     }
    237 
    238 
    239     public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
    240     {
    241         // Change the referenced class.
    242         classConstant.referencedClass =
    243             updateReferencedClass(classConstant.referencedClass);
    244     }
    245 
    246 
    247     // Implementations for AttributeVisitor.
    248 
    249     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
    250 
    251 
    252     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
    253     {
    254         // Change the references of the attributes.
    255         codeAttribute.attributesAccept(clazz, method, this);
    256     }
    257 
    258 
    259     public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
    260     {
    261         // Change the references of the local variables.
    262         localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
    263     }
    264 
    265 
    266     public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
    267     {
    268         // Change the references of the local variables.
    269         localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
    270     }
    271 
    272 
    273     public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
    274     {
    275         // Change the referenced classes.
    276         updateReferencedClasses(signatureAttribute.referencedClasses);
    277     }
    278 
    279 
    280     public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
    281     {
    282         // Change the references of the annotations.
    283         annotationsAttribute.annotationsAccept(clazz, this);
    284     }
    285 
    286 
    287     public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
    288     {
    289         // Change the references of the annotations.
    290         parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
    291     }
    292 
    293 
    294     public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
    295     {
    296         // Change the references of the annotation.
    297         annotationDefaultAttribute.defaultValueAccept(clazz, this);
    298     }
    299 
    300 
    301     // Implementations for LocalVariableInfoVisitor.
    302 
    303     public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
    304     {
    305         // Change the referenced class.
    306         localVariableInfo.referencedClass =
    307             updateReferencedClass(localVariableInfo.referencedClass);
    308     }
    309 
    310 
    311     // Implementations for LocalVariableTypeInfoVisitor.
    312 
    313     public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
    314     {
    315         // Change the referenced classes.
    316         updateReferencedClasses(localVariableTypeInfo.referencedClasses);
    317     }
    318 
    319 
    320     // Implementations for AnnotationVisitor.
    321 
    322     public void visitAnnotation(Clazz clazz, Annotation annotation)
    323     {
    324         // Change the referenced classes.
    325         updateReferencedClasses(annotation.referencedClasses);
    326 
    327         // Change the references of the element values.
    328         annotation.elementValuesAccept(clazz, this);
    329     }
    330 
    331 
    332     // Implementations for ElementValueVisitor.
    333 
    334     public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue)
    335     {
    336         Clazz referencedClass    = elementValue.referencedClass;
    337         Clazz newReferencedClass = updateReferencedClass(referencedClass);
    338         if (referencedClass != newReferencedClass)
    339         {
    340             // Change the referenced annotation class.
    341             elementValue.referencedClass  = newReferencedClass;
    342 
    343             // Change the referenced method.
    344             elementValue.referencedMethod =
    345                 (Method)updateReferencedMember(elementValue.referencedMethod,
    346                                                elementValue.getMethodName(clazz),
    347                                                null,
    348                                                newReferencedClass);
    349         }
    350     }
    351 
    352 
    353     public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
    354     {
    355         // Change the referenced annotation class and method.
    356         visitAnyElementValue(clazz, annotation, constantElementValue);
    357     }
    358 
    359 
    360     public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
    361     {
    362         // Change the referenced annotation class and method.
    363         visitAnyElementValue(clazz, annotation, enumConstantElementValue);
    364 
    365         // Change the referenced classes.
    366         updateReferencedClasses(enumConstantElementValue.referencedClasses);
    367     }
    368 
    369 
    370     public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
    371     {
    372         // Change the referenced annotation class and method.
    373         visitAnyElementValue(clazz, annotation, classElementValue);
    374 
    375         // Change the referenced classes.
    376         updateReferencedClasses(classElementValue.referencedClasses);
    377     }
    378 
    379 
    380     public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
    381     {
    382         // Change the referenced annotation class and method.
    383         visitAnyElementValue(clazz, annotation, annotationElementValue);
    384 
    385         // Change the references of the annotation.
    386         annotationElementValue.annotationAccept(clazz, this);
    387     }
    388 
    389 
    390     public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
    391     {
    392         // Change the referenced annotation class and method.
    393         visitAnyElementValue(clazz, annotation, arrayElementValue);
    394 
    395         // Change the references of the element values.
    396         arrayElementValue.elementValuesAccept(clazz, annotation, this);
    397     }
    398 
    399 
    400     // Small utility methods.
    401 
    402      /**
    403      * Returns whether the given class contains the given interface
    404      * class in its first given number of interfaces.
    405      */
    406     private boolean containsInterfaceClass(Clazz clazz,
    407                                            int   interfaceCount,
    408                                            Clazz interfaceClass)
    409     {
    410         for (int index = 0; index < interfaceCount; index++)
    411         {
    412             if (interfaceClass.equals(clazz.getInterface(index)))
    413             {
    414                 return true;
    415             }
    416         }
    417 
    418         return false;
    419     }
    420 
    421 
    422    /**
    423      * Updates the retargeted classes in the given array of classes.
    424      */
    425     private void updateReferencedClasses(Clazz[] referencedClasses)
    426     {
    427         if (referencedClasses == null)
    428         {
    429             return;
    430         }
    431 
    432         for (int index = 0; index < referencedClasses.length; index++)
    433         {
    434             referencedClasses[index] =
    435                 updateReferencedClass(referencedClasses[index]);
    436         }
    437     }
    438 
    439 
    440     /**
    441      * Returns the retargeted class of the given class.
    442      */
    443     private Clazz updateReferencedClass(Clazz referencedClass)
    444     {
    445         if (referencedClass == null)
    446         {
    447             return null;
    448         }
    449 
    450         Clazz targetClazz = ClassMerger.getTargetClass(referencedClass);
    451         return targetClazz != null ?
    452             targetClazz :
    453             referencedClass;
    454     }
    455 
    456 
    457     /**
    458      * Returns the retargeted class member of the given class member.
    459      */
    460     private Member updateReferencedMember(Member referencedMember,
    461                                           String name,
    462                                           String type,
    463                                           Clazz  newReferencedClass)
    464     {
    465         if (referencedMember == null)
    466         {
    467             return null;
    468         }
    469 
    470         return referencedMember instanceof Field ?
    471             (Member)newReferencedClass.findField(name, type) :
    472             (Member)newReferencedClass.findMethod(name, type);
    473     }
    474 
    475 
    476     /**
    477      * Explicitly adds a new class constant for the given class in the given
    478      * program class.
    479      */
    480     private int addNewClassConstant(ProgramClass programClass,
    481                                     String       className,
    482                                     Clazz        referencedClass)
    483     {
    484         ConstantPoolEditor constantPoolEditor =
    485             new ConstantPoolEditor(programClass);
    486 
    487         int nameIndex =
    488             constantPoolEditor.addUtf8Constant(className);
    489 
    490         int classConstantIndex =
    491             constantPoolEditor.addConstant(new ClassConstant(nameIndex,
    492                                                              referencedClass));
    493         return classConstantIndex;
    494     }
    495 }