Home | History | Annotate | Download | only in evaluation
      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.evaluation;
     22 
     23 import proguard.classfile.*;
     24 import proguard.classfile.attribute.*;
     25 import proguard.classfile.attribute.visitor.*;
     26 import proguard.classfile.constant.*;
     27 import proguard.classfile.constant.visitor.ConstantVisitor;
     28 import proguard.classfile.editor.*;
     29 import proguard.classfile.instruction.visitor.InstructionVisitor;
     30 import proguard.classfile.util.*;
     31 import proguard.classfile.visitor.*;
     32 import proguard.optimize.info.*;
     33 
     34 /**
     35  * This ClassVisitor simplifies the descriptors that contain simple enums in
     36  * the program classes that it visits.
     37  *
     38  * @see SimpleEnumMarker
     39  * @see MemberReferenceFixer
     40  * @author Eric Lafortune
     41  */
     42 public class SimpleEnumDescriptorSimplifier
     43 extends      SimplifiedVisitor
     44 implements   ClassVisitor,
     45              ConstantVisitor,
     46              MemberVisitor,
     47              AttributeVisitor,
     48              LocalVariableInfoVisitor,
     49              LocalVariableTypeInfoVisitor
     50 {
     51     //*
     52     private static final boolean DEBUG = false;
     53     /*/
     54     private static       boolean DEBUG = System.getProperty("enum") != null;
     55     //*/
     56 
     57 
     58     // Implementations for ClassVisitor.
     59 
     60     public void visitProgramClass(ProgramClass programClass)
     61     {
     62         if (DEBUG)
     63         {
     64             System.out.println("SimpleEnumDescriptorSimplifier: "+programClass.getName());
     65         }
     66 
     67         // Simplify the class members.
     68         programClass.fieldsAccept(this);
     69         programClass.methodsAccept(this);
     70 
     71         // Simplify the attributes.
     72         //programClass.attributesAccept(this);
     73 
     74         // Simplify the simple enum array constants.
     75         programClass.constantPoolEntriesAccept(this);
     76     }
     77 
     78 
     79     // Implementations for ConstantVisitor.
     80 
     81     public void visitAnyConstant(Clazz clazz, Constant constant) {}
     82 
     83 
     84     public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
     85     {
     86         // Does the constant refer to a simple enum type?
     87         Clazz referencedClass = stringConstant.referencedClass;
     88         if (isSimpleEnum(referencedClass))
     89         {
     90             // Is it an array type?
     91             String name = stringConstant.getString(clazz);
     92             if (ClassUtil.isInternalArrayType(name))
     93             {
     94                 // Update the type.
     95                 ConstantPoolEditor constantPoolEditor =
     96                     new ConstantPoolEditor((ProgramClass)clazz);
     97 
     98                 String newName = simplifyDescriptor(name, referencedClass);
     99 
    100                 stringConstant.u2stringIndex =
    101                     constantPoolEditor.addUtf8Constant(newName);
    102 
    103                 // Clear the referenced class.
    104                 stringConstant.referencedClass = null;
    105             }
    106         }
    107     }
    108 
    109 
    110     public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant)
    111     {
    112         // Update the descriptor if it has any simple enum classes.
    113         String descriptor    = invokeDynamicConstant.getType(clazz);
    114         String newDescriptor = simplifyDescriptor(descriptor, invokeDynamicConstant.referencedClasses);
    115 
    116         if (!descriptor.equals(newDescriptor))
    117         {
    118             // Update the descriptor.
    119             ConstantPoolEditor constantPoolEditor =
    120                 new ConstantPoolEditor((ProgramClass)clazz);
    121 
    122             invokeDynamicConstant.u2nameAndTypeIndex =
    123                 constantPoolEditor.addNameAndTypeConstant(invokeDynamicConstant.getName(clazz),
    124                                                           newDescriptor);
    125 
    126             // Update the referenced classes.
    127             invokeDynamicConstant.referencedClasses =
    128                 simplifyReferencedClasses(descriptor, invokeDynamicConstant.referencedClasses);
    129         }
    130     }
    131 
    132 
    133     public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
    134     {
    135         // Does the constant refer to a simple enum type?
    136         Clazz referencedClass = classConstant.referencedClass;
    137         if (isSimpleEnum(referencedClass))
    138         {
    139             // Is it an array type?
    140             String name = classConstant.getName(clazz);
    141             if (ClassUtil.isInternalArrayType(name))
    142             {
    143                 // Update the type.
    144                 ConstantPoolEditor constantPoolEditor =
    145                     new ConstantPoolEditor((ProgramClass)clazz);
    146 
    147                 String newName = simplifyDescriptor(name, referencedClass);
    148 
    149                 classConstant.u2nameIndex =
    150                     constantPoolEditor.addUtf8Constant(newName);
    151 
    152                 // Clear the referenced class.
    153                 classConstant.referencedClass = null;
    154             }
    155         }
    156     }
    157 
    158 
    159     // Implementations for MemberVisitor.
    160 
    161     public void visitProgramField(ProgramClass programClass, ProgramField programField)
    162     {
    163         // Update the descriptor if it has a simple enum class.
    164         String descriptor    = programField.getDescriptor(programClass);
    165         String newDescriptor = simplifyDescriptor(descriptor, programField.referencedClass);
    166 
    167         if (!descriptor.equals(newDescriptor))
    168         {
    169             String name    = programField.getName(programClass);
    170             String newName = name + ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode()));
    171 
    172             if (DEBUG)
    173             {
    174                 System.out.println("SimpleEnumDescriptorSimplifier: ["+programClass.getName()+"."+name+" "+descriptor + "] -> ["+newName+" "+newDescriptor+"]");
    175             }
    176 
    177             ConstantPoolEditor constantPoolEditor =
    178                 new ConstantPoolEditor(programClass);
    179 
    180             // Update the name.
    181             programField.u2nameIndex =
    182                 constantPoolEditor.addUtf8Constant(newName);
    183 
    184             // Update the descriptor itself.
    185             programField.u2descriptorIndex =
    186                 constantPoolEditor.addUtf8Constant(newDescriptor);
    187 
    188             // Clear the referenced class.
    189             programField.referencedClass = null;
    190 
    191             // Clear the field value.
    192             FieldOptimizationInfo fieldOptimizationInfo =
    193                 FieldOptimizationInfo.getFieldOptimizationInfo(programField);
    194             if (fieldOptimizationInfo != null)
    195             {
    196                 fieldOptimizationInfo.resetValue(programClass, programField);
    197             }
    198 
    199             // Simplify the signature.
    200             programField.attributesAccept(programClass, this);
    201         }
    202     }
    203 
    204 
    205     public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
    206     {
    207 //        // Skip the valueOf method.
    208 //        if (programMethod.getName(programClass).equals(ClassConstants.METHOD_NAME_VALUEOF))
    209 //        {
    210 //            return;
    211 //        }
    212 
    213         // Simplify the code, the signature, and the parameter annotations,
    214         // before simplifying the descriptor.
    215         programMethod.attributesAccept(programClass, this);
    216 
    217         // Update the descriptor if it has any simple enum classes.
    218         String descriptor    = programMethod.getDescriptor(programClass);
    219         String newDescriptor = simplifyDescriptor(descriptor, programMethod.referencedClasses);
    220 
    221         if (!descriptor.equals(newDescriptor))
    222         {
    223             String name    = programMethod.getName(programClass);
    224             String newName = name;
    225 
    226             // Append a code, if the method isn't a class instance initializer.
    227             if (!name.equals(ClassConstants.METHOD_NAME_INIT))
    228             {
    229                 newName += ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode()));
    230             }
    231 
    232             if (DEBUG)
    233             {
    234                 System.out.println("SimpleEnumDescriptorSimplifier: ["+programClass.getName()+"."+name+descriptor+"] -> ["+newName+newDescriptor+"]");
    235             }
    236 
    237             ConstantPoolEditor constantPoolEditor =
    238                 new ConstantPoolEditor(programClass);
    239 
    240             // Update the name, if necessary.
    241             if (!newName.equals(name))
    242             {
    243                 programMethod.u2nameIndex =
    244                     constantPoolEditor.addUtf8Constant(newName);
    245             }
    246 
    247             // Update the descriptor itself.
    248             programMethod.u2descriptorIndex =
    249                 constantPoolEditor.addUtf8Constant(newDescriptor);
    250 
    251             // Update the referenced classes.
    252             programMethod.referencedClasses =
    253                 simplifyReferencedClasses(descriptor, programMethod.referencedClasses);
    254         }
    255     }
    256 
    257 
    258     // Implementations for AttributeVisitor.
    259 
    260     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
    261 
    262 
    263     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
    264     {
    265         // Simplify the local variable descriptors.
    266         codeAttribute.attributesAccept(clazz, method, this);
    267     }
    268 
    269 
    270     public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
    271     {
    272         // Change the references of the local variables.
    273         localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
    274     }
    275 
    276 
    277     public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
    278     {
    279         // Change the references of the local variables.
    280         localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
    281     }
    282 
    283 
    284     public void visitSignatureAttribute(Clazz clazz, Field field, SignatureAttribute signatureAttribute)
    285     {
    286         // We're only looking at the base type for now.
    287         if (signatureAttribute.referencedClasses != null &&
    288             signatureAttribute.referencedClasses.length > 0)
    289         {
    290             // Update the signature if it has any simple enum classes.
    291             String signature    = signatureAttribute.getSignature(clazz);
    292             String newSignature = simplifyDescriptor(signature,
    293                                                      signatureAttribute.referencedClasses[0]);
    294 
    295             if (!signature.equals(newSignature))
    296             {
    297                 // Update the signature.
    298                 signatureAttribute.u2signatureIndex =
    299                     new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature);
    300 
    301                 // Clear the referenced class.
    302                 signatureAttribute.referencedClasses[0] = null;
    303             }
    304         }
    305     }
    306 
    307 
    308     public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute)
    309     {
    310         // Compute the new signature.
    311         String signature    = signatureAttribute.getSignature(clazz);
    312         String newSignature = simplifyDescriptor(signature,
    313                                                  signatureAttribute.referencedClasses);
    314 
    315         if (!signature.equals(newSignature))
    316         {
    317             // Update the signature.
    318             signatureAttribute.u2signatureIndex =
    319                 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature);
    320         }
    321     }
    322 
    323 
    324    // Implementations for LocalVariableInfoVisitor.
    325 
    326     public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
    327     {
    328         // Update the descriptor if it has a simple enum class.
    329         String descriptor    = localVariableInfo.getDescriptor(clazz);
    330         String newDescriptor = simplifyDescriptor(descriptor, localVariableInfo.referencedClass);
    331 
    332         if (!descriptor.equals(newDescriptor))
    333         {
    334             // Update the descriptor.
    335             localVariableInfo.u2descriptorIndex =
    336                 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newDescriptor);
    337 
    338             // Clear the referenced class.
    339             localVariableInfo.referencedClass = null;
    340         }
    341     }
    342 
    343 
    344     // Implementations for LocalVariableTypeInfoVisitor.
    345 
    346     public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
    347     {
    348         // We're only looking at the base type for now.
    349         if (localVariableTypeInfo.referencedClasses != null &&
    350             localVariableTypeInfo.referencedClasses.length > 0)
    351         {
    352             // Update the signature if it has any simple enum classes.
    353             String signature    = localVariableTypeInfo.getSignature(clazz);
    354             String newSignature = simplifyDescriptor(signature,
    355                                                      localVariableTypeInfo.referencedClasses[0]);
    356 
    357             if (!signature.equals(newSignature))
    358             {
    359                 // Update the signature.
    360                 localVariableTypeInfo.u2signatureIndex =
    361                     new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature);
    362 
    363                 // Clear the referenced class.
    364                 localVariableTypeInfo.referencedClasses[0] = null;
    365             }
    366         }
    367     }
    368 
    369 
    370     // Small utility methods.
    371 
    372     /**
    373      * Returns the descriptor with simplified enum type.
    374      */
    375     private String simplifyDescriptor(String descriptor,
    376                                       Clazz  referencedClass)
    377     {
    378         return isSimpleEnum(referencedClass) ?
    379                    descriptor.substring(0, ClassUtil.internalArrayTypeDimensionCount(descriptor)) + ClassConstants.TYPE_INT :
    380                    descriptor;
    381     }
    382 
    383 
    384     /**
    385      * Returns the descriptor with simplified enum types.
    386      */
    387     private String simplifyDescriptor(String  descriptor,
    388                                       Clazz[] referencedClasses)
    389     {
    390         if (referencedClasses != null)
    391         {
    392             InternalTypeEnumeration internalTypeEnumeration =
    393                 new InternalTypeEnumeration(descriptor);
    394 
    395             int referencedClassIndex = 0;
    396 
    397             StringBuffer newDescriptorBuffer =
    398                 new StringBuffer(descriptor.length());
    399 
    400             // Go over the formal type parameters.
    401             {
    402                 // Consider the classes referenced by this formal type
    403                 // parameter.
    404                 String type  = internalTypeEnumeration.formalTypeParameters();
    405                 int    count = new DescriptorClassEnumeration(type).classCount();
    406 
    407                 // Replace simple enum types.
    408                 for (int counter = 0; counter < count; counter++)
    409                 {
    410                     Clazz referencedClass =
    411                         referencedClasses[referencedClassIndex++];
    412 
    413                     if (isSimpleEnum(referencedClass))
    414                     {
    415                         // Let's replace the simple enum type by
    416                         // java.lang.Integer.
    417                         type =
    418                             type.substring(0, ClassUtil.internalArrayTypeDimensionCount(type)) +
    419                             ClassConstants.NAME_JAVA_LANG_INTEGER;
    420                     }
    421                 }
    422 
    423                 newDescriptorBuffer.append(type);
    424             }
    425 
    426             newDescriptorBuffer.append(ClassConstants.METHOD_ARGUMENTS_OPEN);
    427 
    428             // Go over the parameters.
    429             while (internalTypeEnumeration.hasMoreTypes())
    430             {
    431                 // Consider the classes referenced by this parameter type.
    432                 String type  = internalTypeEnumeration.nextType();
    433                 int    count = new DescriptorClassEnumeration(type).classCount();
    434 
    435                 // Replace simple enum types.
    436                 for (int counter = 0; counter < count; counter++)
    437                 {
    438                     Clazz referencedClass =
    439                         referencedClasses[referencedClassIndex++];
    440 
    441                     if (isSimpleEnum(referencedClass))
    442                     {
    443                         type =
    444                             type.substring(0, ClassUtil.internalArrayTypeDimensionCount(type)) +
    445                             ClassConstants.TYPE_INT;
    446                     }
    447                 }
    448 
    449                 newDescriptorBuffer.append(type);
    450             }
    451 
    452             newDescriptorBuffer.append(ClassConstants.METHOD_ARGUMENTS_CLOSE);
    453 
    454             // Go over the return value.
    455             {
    456                 String type  = internalTypeEnumeration.returnType();
    457                 int    count = new DescriptorClassEnumeration(type).classCount();
    458 
    459                 // Replace simple enum types.
    460                 for (int counter = 0; counter < count; counter++)
    461                 {
    462                     Clazz referencedClass =
    463                         referencedClasses[referencedClassIndex++];
    464 
    465                     if (isSimpleEnum(referencedClass))
    466                     {
    467                         type =
    468                             type.substring(0, ClassUtil.internalArrayTypeDimensionCount(type)) +
    469                             ClassConstants.TYPE_INT;
    470                     }
    471                 }
    472 
    473                 newDescriptorBuffer.append(type);
    474             }
    475 
    476             descriptor = newDescriptorBuffer.toString();
    477         }
    478 
    479         return descriptor;
    480     }
    481 
    482 
    483     /**
    484      * Returns the simplified and shrunk array of referenced classes for the
    485      * given descriptor.
    486      */
    487     private Clazz[] simplifyReferencedClasses(String  descriptor,
    488                                               Clazz[] referencedClasses)
    489     {
    490         if (referencedClasses != null)
    491         {
    492             InternalTypeEnumeration internalTypeEnumeration =
    493                 new InternalTypeEnumeration(descriptor);
    494 
    495             int referencedClassIndex    = 0;
    496             int newReferencedClassIndex = 0;
    497 
    498             // Go over the formal type parameters.
    499             {
    500                 String type  = internalTypeEnumeration.formalTypeParameters();
    501                 int    count = new DescriptorClassEnumeration(type).classCount();
    502 
    503                 // Clear all non-simple enum classes
    504                 // (now java.lang.Integer).
    505                 for (int counter = 0; counter < count; counter++)
    506                 {
    507                     Clazz referencedClass =
    508                         referencedClasses[referencedClassIndex++];
    509 
    510                     referencedClasses[newReferencedClassIndex++] =
    511                         isSimpleEnum(referencedClass) ? null : referencedClass;
    512                 }
    513             }
    514 
    515             // Go over the parameters.
    516             while (internalTypeEnumeration.hasMoreTypes())
    517             {
    518                 // Consider the classes referenced by this parameter type.
    519                 String type  = internalTypeEnumeration.nextType();
    520                 int    count = new DescriptorClassEnumeration(type).classCount();
    521 
    522                 // Copy all non-simple enum classes.
    523                 for (int counter = 0; counter < count; counter++)
    524                 {
    525                     Clazz referencedClass =
    526                         referencedClasses[referencedClassIndex++];
    527 
    528                     if (!isSimpleEnum(referencedClass))
    529                     {
    530                         referencedClasses[newReferencedClassIndex++] =
    531                             referencedClass;
    532                     }
    533                 }
    534             }
    535 
    536             // Go over the return type.
    537             {
    538                 String type  = internalTypeEnumeration.returnType();
    539                 int    count = new DescriptorClassEnumeration(type).classCount();
    540 
    541                 // Copy all non-simple enum classes.
    542                 for (int counter = 0; counter < count; counter++)
    543                 {
    544                     Clazz referencedClass =
    545                         referencedClasses[referencedClassIndex++];
    546 
    547                     if (!isSimpleEnum(referencedClass))
    548                     {
    549                         referencedClasses[newReferencedClassIndex++] =
    550                             referencedClass;
    551                     }
    552                 }
    553             }
    554 
    555             // Shrink the array to the proper size.
    556             if (newReferencedClassIndex == 0)
    557             {
    558                 referencedClasses = null;
    559             }
    560             else if (newReferencedClassIndex < referencedClassIndex)
    561             {
    562                 Clazz[] newReferencedClasses = new Clazz[newReferencedClassIndex];
    563                 System.arraycopy(referencedClasses, 0,
    564                                  newReferencedClasses, 0,
    565                                  newReferencedClassIndex);
    566 
    567                 referencedClasses = newReferencedClasses;
    568             }
    569         }
    570 
    571         return referencedClasses;
    572     }
    573 
    574 
    575     /**
    576      * Returns whether the given class is not null and a simple enum class.
    577      */
    578     private boolean isSimpleEnum(Clazz clazz)
    579     {
    580         return clazz != null &&
    581                SimpleEnumMarker.isSimpleEnum(clazz);
    582     }
    583 }
    584