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.preverification.*;
     28 import proguard.classfile.attribute.visitor.*;
     29 import proguard.classfile.constant.*;
     30 import proguard.classfile.constant.visitor.ConstantVisitor;
     31 import proguard.classfile.util.SimplifiedVisitor;
     32 import proguard.classfile.visitor.*;
     33 
     34 import java.util.Arrays;
     35 
     36 
     37 /**
     38  * This ClassVisitor removes UTF-8 constant pool entries that are not used.
     39  *
     40  * @author Eric Lafortune
     41  */
     42 public class Utf8Shrinker
     43 extends      SimplifiedVisitor
     44 implements   ClassVisitor,
     45              MemberVisitor,
     46              ConstantVisitor,
     47              AttributeVisitor,
     48              InnerClassesInfoVisitor,
     49              ParameterInfoVisitor,
     50              LocalVariableInfoVisitor,
     51              LocalVariableTypeInfoVisitor,
     52              AnnotationVisitor,
     53              ElementValueVisitor
     54 {
     55     // A visitor info flag to indicate the UTF-8 constant pool entry is being used.
     56     private static final Object USED = new Object();
     57 
     58     private       int[]                constantIndexMap     = new int[ClassConstants.TYPICAL_CONSTANT_POOL_SIZE];
     59     private final ConstantPoolRemapper constantPoolRemapper = new ConstantPoolRemapper();
     60 
     61 
     62     // Implementations for ClassVisitor.
     63 
     64     public void visitProgramClass(ProgramClass programClass)
     65     {
     66         // Mark the UTF-8 entries referenced by the other constant pool entries.
     67         programClass.constantPoolEntriesAccept(this);
     68 
     69         // Mark the UTF-8 entries referenced by the fields and methods.
     70         programClass.fieldsAccept(this);
     71         programClass.methodsAccept(this);
     72 
     73         // Mark the UTF-8 entries referenced by the attributes.
     74         programClass.attributesAccept(this);
     75 
     76         // Shift the used constant pool entries together, filling out the
     77         // index map.
     78         int newConstantPoolCount =
     79             shrinkConstantPool(programClass.constantPool,
     80                                programClass.u2constantPoolCount);
     81 
     82         // Remap the references to the constant pool if it has shrunk.
     83         if (newConstantPoolCount < programClass.u2constantPoolCount)
     84         {
     85             programClass.u2constantPoolCount = newConstantPoolCount;
     86 
     87             // Remap all constant pool references.
     88             constantPoolRemapper.setConstantIndexMap(constantIndexMap);
     89             constantPoolRemapper.visitProgramClass(programClass);
     90         }
     91     }
     92 
     93 
     94     // Implementations for MemberVisitor.
     95 
     96     public void visitProgramMember(ProgramClass programClass, ProgramMember programMember)
     97     {
     98         // Mark the name and descriptor UTF-8 entries.
     99         markCpUtf8Entry(programClass, programMember.u2nameIndex);
    100         markCpUtf8Entry(programClass, programMember.u2descriptorIndex);
    101 
    102         // Mark the UTF-8 entries referenced by the attributes.
    103         programMember.attributesAccept(programClass, this);
    104     }
    105 
    106 
    107     // Implementations for ConstantVisitor.
    108 
    109     public void visitAnyConstant(Clazz clazz, Constant constant) {}
    110 
    111 
    112     public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
    113     {
    114         markCpUtf8Entry(clazz, stringConstant.u2stringIndex);
    115     }
    116 
    117 
    118     public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
    119     {
    120         markCpUtf8Entry(clazz, classConstant.u2nameIndex);
    121     }
    122 
    123 
    124     public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)
    125     {
    126         markCpUtf8Entry(clazz, nameAndTypeConstant.u2nameIndex);
    127         markCpUtf8Entry(clazz, nameAndTypeConstant.u2descriptorIndex);
    128     }
    129 
    130 
    131     // Implementations for AttributeVisitor.
    132 
    133     public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute)
    134     {
    135         // This is the best we can do for unknown attributes.
    136         markCpUtf8Entry(clazz, unknownAttribute.u2attributeNameIndex);
    137     }
    138 
    139 
    140     public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute)
    141     {
    142         markCpUtf8Entry(clazz, sourceFileAttribute.u2attributeNameIndex);
    143 
    144         markCpUtf8Entry(clazz, sourceFileAttribute.u2sourceFileIndex);
    145     }
    146 
    147 
    148     public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute)
    149     {
    150         markCpUtf8Entry(clazz, sourceDirAttribute.u2attributeNameIndex);
    151 
    152         markCpUtf8Entry(clazz, sourceDirAttribute.u2sourceDirIndex);
    153     }
    154 
    155 
    156     public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
    157     {
    158         markCpUtf8Entry(clazz, innerClassesAttribute.u2attributeNameIndex);
    159 
    160         // Mark the UTF-8 entries referenced by the inner classes.
    161         innerClassesAttribute.innerClassEntriesAccept(clazz, this);
    162     }
    163 
    164 
    165     public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
    166     {
    167         markCpUtf8Entry(clazz, enclosingMethodAttribute.u2attributeNameIndex);
    168 
    169         // These entries have already been marked in the constant pool.
    170         //clazz.constantPoolEntryAccept(this, enclosingMethodAttribute.u2classIndex);
    171         //clazz.constantPoolEntryAccept(this, enclosingMethodAttribute.u2nameAndTypeIndex);
    172     }
    173 
    174 
    175     public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute)
    176     {
    177         markCpUtf8Entry(clazz, deprecatedAttribute.u2attributeNameIndex);
    178     }
    179 
    180 
    181     public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute)
    182     {
    183         markCpUtf8Entry(clazz, syntheticAttribute.u2attributeNameIndex);
    184     }
    185 
    186 
    187     public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
    188     {
    189         markCpUtf8Entry(clazz, signatureAttribute.u2attributeNameIndex);
    190 
    191         markCpUtf8Entry(clazz, signatureAttribute.u2signatureIndex);
    192     }
    193 
    194 
    195     public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute)
    196     {
    197         markCpUtf8Entry(clazz, constantValueAttribute.u2attributeNameIndex);
    198     }
    199 
    200 
    201     public void visitMethodParametersAttribute(Clazz clazz, Method method, MethodParametersAttribute methodParametersAttribute)
    202     {
    203         markCpUtf8Entry(clazz, methodParametersAttribute.u2attributeNameIndex);
    204 
    205         // Mark the UTF-8 entries referenced by the parameter information.
    206         methodParametersAttribute.parametersAccept(clazz, method, this);
    207     }
    208 
    209 
    210     public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute)
    211     {
    212         markCpUtf8Entry(clazz, exceptionsAttribute.u2attributeNameIndex);
    213     }
    214 
    215 
    216     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
    217     {
    218         markCpUtf8Entry(clazz, codeAttribute.u2attributeNameIndex);
    219 
    220         // Mark the UTF-8 entries referenced by the attributes.
    221         codeAttribute.attributesAccept(clazz, method, this);
    222     }
    223 
    224 
    225     public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
    226     {
    227         markCpUtf8Entry(clazz, stackMapAttribute.u2attributeNameIndex);
    228     }
    229 
    230 
    231     public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
    232     {
    233         markCpUtf8Entry(clazz, stackMapTableAttribute.u2attributeNameIndex);
    234     }
    235 
    236 
    237     public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute)
    238     {
    239         markCpUtf8Entry(clazz, lineNumberTableAttribute.u2attributeNameIndex);
    240     }
    241 
    242 
    243     public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
    244     {
    245         markCpUtf8Entry(clazz, localVariableTableAttribute.u2attributeNameIndex);
    246 
    247         // Mark the UTF-8 entries referenced by the local variables.
    248         localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
    249     }
    250 
    251 
    252     public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
    253     {
    254         markCpUtf8Entry(clazz, localVariableTypeTableAttribute.u2attributeNameIndex);
    255 
    256         // Mark the UTF-8 entries referenced by the local variable types.
    257         localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
    258     }
    259 
    260 
    261     public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
    262     {
    263         markCpUtf8Entry(clazz, annotationsAttribute.u2attributeNameIndex);
    264 
    265         // Mark the UTF-8 entries referenced by the annotations.
    266         annotationsAttribute.annotationsAccept(clazz, this);
    267     }
    268 
    269 
    270     public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
    271     {
    272         markCpUtf8Entry(clazz, parameterAnnotationsAttribute.u2attributeNameIndex);
    273 
    274         // Mark the UTF-8 entries referenced by the annotations.
    275         parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
    276     }
    277 
    278 
    279     public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
    280     {
    281         markCpUtf8Entry(clazz, annotationDefaultAttribute.u2attributeNameIndex);
    282 
    283         // Mark the UTF-8 entries referenced by the element value.
    284         annotationDefaultAttribute.defaultValueAccept(clazz, this);
    285     }
    286 
    287 
    288     // Implementations for InnerClassesInfoVisitor.
    289 
    290     public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo)
    291     {
    292         if (innerClassesInfo.u2innerNameIndex != 0)
    293         {
    294             markCpUtf8Entry(clazz, innerClassesInfo.u2innerNameIndex);
    295         }
    296     }
    297 
    298 
    299     // Implementations for ParameterInfoVisitor.
    300 
    301     public void visitParameterInfo(Clazz clazz, Method method, int parameterIndex, ParameterInfo parameterInfo)
    302     {
    303         if (parameterInfo.u2nameIndex != 0)
    304         {
    305             markCpUtf8Entry(clazz, parameterInfo.u2nameIndex);
    306         }
    307     }
    308 
    309 
    310     // Implementations for LocalVariableInfoVisitor.
    311 
    312     public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
    313     {
    314         markCpUtf8Entry(clazz, localVariableInfo.u2nameIndex);
    315         markCpUtf8Entry(clazz, localVariableInfo.u2descriptorIndex);
    316     }
    317 
    318 
    319     // Implementations for LocalVariableTypeInfoVisitor.
    320 
    321     public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
    322     {
    323         markCpUtf8Entry(clazz, localVariableTypeInfo.u2nameIndex);
    324         markCpUtf8Entry(clazz, localVariableTypeInfo.u2signatureIndex);
    325     }
    326 
    327 
    328     // Implementations for AnnotationVisitor.
    329 
    330     public void visitAnnotation(Clazz clazz, Annotation annotation)
    331     {
    332         markCpUtf8Entry(clazz, annotation.u2typeIndex);
    333 
    334         // Mark the UTF-8 entries referenced by the element values.
    335         annotation.elementValuesAccept(clazz, this);
    336     }
    337 
    338 
    339     // Implementations for ElementValueVisitor.
    340 
    341     public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
    342     {
    343         if (constantElementValue.u2elementNameIndex != 0)
    344         {
    345             markCpUtf8Entry(clazz, constantElementValue.u2elementNameIndex);
    346         }
    347 
    348         // Only the string constant element value refers to a UTF-8 entry.
    349         if (constantElementValue.u1tag == ClassConstants.ELEMENT_VALUE_STRING_CONSTANT)
    350         {
    351             markCpUtf8Entry(clazz, constantElementValue.u2constantValueIndex);
    352         }
    353     }
    354 
    355 
    356     public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
    357     {
    358         if (enumConstantElementValue.u2elementNameIndex != 0)
    359         {
    360             markCpUtf8Entry(clazz, enumConstantElementValue.u2elementNameIndex);
    361         }
    362 
    363         markCpUtf8Entry(clazz, enumConstantElementValue.u2typeNameIndex);
    364         markCpUtf8Entry(clazz, enumConstantElementValue.u2constantNameIndex);
    365     }
    366 
    367 
    368     public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
    369     {
    370         if (classElementValue.u2elementNameIndex != 0)
    371         {
    372             markCpUtf8Entry(clazz, classElementValue.u2elementNameIndex);
    373         }
    374 
    375         markCpUtf8Entry(clazz, classElementValue.u2classInfoIndex);
    376     }
    377 
    378 
    379     public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
    380     {
    381         if (annotationElementValue.u2elementNameIndex != 0)
    382         {
    383             markCpUtf8Entry(clazz, annotationElementValue.u2elementNameIndex);
    384         }
    385 
    386         // Mark the UTF-8 entries referenced by the annotation.
    387         annotationElementValue.annotationAccept(clazz, this);
    388     }
    389 
    390 
    391     public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
    392     {
    393         if (arrayElementValue.u2elementNameIndex != 0)
    394         {
    395             markCpUtf8Entry(clazz, arrayElementValue.u2elementNameIndex);
    396         }
    397 
    398         // Mark the UTF-8 entries referenced by the element values.
    399         arrayElementValue.elementValuesAccept(clazz, annotation, this);
    400     }
    401 
    402 
    403     // Small utility methods.
    404 
    405     /**
    406      * Marks the given UTF-8 constant pool entry of the given class.
    407      */
    408     private void markCpUtf8Entry(Clazz clazz, int index)
    409     {
    410          markAsUsed((Utf8Constant)((ProgramClass)clazz).getConstant(index));
    411     }
    412 
    413 
    414     /**
    415      * Marks the given VisitorAccepter as being used.
    416      * In this context, the VisitorAccepter will be a Utf8Constant object.
    417      */
    418     private void markAsUsed(VisitorAccepter visitorAccepter)
    419     {
    420         visitorAccepter.setVisitorInfo(USED);
    421     }
    422 
    423 
    424     /**
    425      * Returns whether the given VisitorAccepter has been marked as being used.
    426      * In this context, the VisitorAccepter will be a Utf8Constant object.
    427      */
    428     private boolean isUsed(VisitorAccepter visitorAccepter)
    429     {
    430         return visitorAccepter.getVisitorInfo() == USED;
    431     }
    432 
    433 
    434     /**
    435      * Removes all UTF-8 entries that are not marked as being used
    436      * from the given constant pool.
    437      * @return the new number of entries.
    438      */
    439     private int shrinkConstantPool(Constant[] constantPool, int length)
    440     {
    441         // Create a new index map, if necessary.
    442         if (constantIndexMap.length < length)
    443         {
    444             constantIndexMap = new int[length];
    445         }
    446 
    447         int     counter = 1;
    448         boolean isUsed  = false;
    449 
    450         // Shift the used constant pool entries together.
    451         for (int index = 1; index < length; index++)
    452         {
    453             Constant constant = constantPool[index];
    454 
    455             // Is the constant being used? Don't update the flag if this is the
    456             // second half of a long entry.
    457             if (constant != null)
    458             {
    459                 isUsed = constant.getTag() != ClassConstants.CONSTANT_Utf8 ||
    460                          isUsed(constant);
    461             }
    462 
    463             if (isUsed)
    464             {
    465                 // Remember the new index.
    466                 constantIndexMap[index] = counter;
    467 
    468                 // Shift the constant pool entry.
    469                 constantPool[counter++] = constant;
    470             }
    471             else
    472             {
    473                 // Remember an invalid index.
    474                 constantIndexMap[index] = -1;
    475             }
    476         }
    477 
    478         // Clear the remaining constant pool elements.
    479         Arrays.fill(constantPool, counter, length, null);
    480 
    481         return counter;
    482     }
    483 }
    484