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.preverification.visitor.*;
     29 import proguard.classfile.attribute.visitor.*;
     30 import proguard.classfile.constant.*;
     31 import proguard.classfile.constant.visitor.ConstantVisitor;
     32 import proguard.classfile.instruction.*;
     33 import proguard.classfile.instruction.visitor.InstructionVisitor;
     34 import proguard.classfile.util.SimplifiedVisitor;
     35 import proguard.classfile.visitor.*;
     36 
     37 import java.util.Arrays;
     38 
     39 /**
     40  * This ClassVisitor removes all unused entries from the constant pool.
     41  *
     42  * @author Eric Lafortune
     43  */
     44 public class ConstantPoolShrinker
     45 extends      SimplifiedVisitor
     46 implements   ClassVisitor,
     47              MemberVisitor,
     48              ConstantVisitor,
     49              AttributeVisitor,
     50              BootstrapMethodInfoVisitor,
     51              InnerClassesInfoVisitor,
     52              ExceptionInfoVisitor,
     53              StackMapFrameVisitor,
     54              VerificationTypeVisitor,
     55              ParameterInfoVisitor,
     56              LocalVariableInfoVisitor,
     57              LocalVariableTypeInfoVisitor,
     58              AnnotationVisitor,
     59              ElementValueVisitor,
     60              InstructionVisitor
     61 {
     62     // A visitor info flag to indicate the constant is being used.
     63     private static final Object USED = new Object();
     64 
     65     private       int[]                constantIndexMap     = new int[ClassConstants.TYPICAL_CONSTANT_POOL_SIZE];
     66     private final ConstantPoolRemapper constantPoolRemapper = new ConstantPoolRemapper();
     67 
     68 
     69     // Implementations for ClassVisitor.
     70 
     71     public void visitProgramClass(ProgramClass programClass)
     72     {
     73         // Mark this class's name.
     74         markConstant(programClass, programClass.u2thisClass);
     75 
     76         // Mark the superclass class constant.
     77         programClass.superClassConstantAccept(this);
     78 
     79         // Mark the interface class constants.
     80         programClass.interfaceConstantsAccept(this);
     81 
     82         // Mark the constants referenced by the class members.
     83         programClass.fieldsAccept(this);
     84         programClass.methodsAccept(this);
     85 
     86         // Mark the attributes.
     87         programClass.attributesAccept(this);
     88 
     89         // Shift the used constant pool entries together, filling out the
     90         // index map.
     91         int newConstantPoolCount =
     92             shrinkConstantPool(programClass.constantPool,
     93                                programClass.u2constantPoolCount);
     94 
     95         // Remap the references to the constant pool if it has shrunk.
     96         if (newConstantPoolCount < programClass.u2constantPoolCount)
     97         {
     98             programClass.u2constantPoolCount = newConstantPoolCount;
     99 
    100             // Remap all constant pool references.
    101             constantPoolRemapper.setConstantIndexMap(constantIndexMap);
    102             constantPoolRemapper.visitProgramClass(programClass);
    103         }
    104     }
    105 
    106 
    107     // Implementations for MemberVisitor.
    108 
    109     public void visitProgramMember(ProgramClass programClass, ProgramMember programMember)
    110     {
    111         // Mark the name and descriptor.
    112         markConstant(programClass, programMember.u2nameIndex);
    113         markConstant(programClass, programMember.u2descriptorIndex);
    114 
    115         // Mark the attributes.
    116         programMember.attributesAccept(programClass, this);
    117     }
    118 
    119 
    120     // Implementations for ConstantVisitor.
    121 
    122     public void visitAnyConstant(Clazz clazz, Constant constant)
    123     {
    124         markAsUsed(constant);
    125     }
    126 
    127 
    128     public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
    129     {
    130         markAsUsed(stringConstant);
    131 
    132         markConstant(clazz, stringConstant.u2stringIndex);
    133     }
    134 
    135 
    136     public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant)
    137     {
    138         markAsUsed(invokeDynamicConstant);
    139 
    140         markConstant(clazz, invokeDynamicConstant.u2nameAndTypeIndex);
    141 
    142         // Mark the bootstrap methods attribute.
    143         clazz.attributesAccept(this);
    144     }
    145 
    146 
    147     public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant)
    148     {
    149         markAsUsed(methodHandleConstant);
    150 
    151         markConstant(clazz, methodHandleConstant.u2referenceIndex);
    152     }
    153 
    154 
    155     public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
    156     {
    157         markAsUsed(refConstant);
    158 
    159         markConstant(clazz, refConstant.u2classIndex);
    160         markConstant(clazz, refConstant.u2nameAndTypeIndex);
    161     }
    162 
    163 
    164     public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
    165     {
    166         markAsUsed(classConstant);
    167 
    168         markConstant(clazz, classConstant.u2nameIndex);
    169     }
    170 
    171 
    172     public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant)
    173     {
    174         markAsUsed(methodTypeConstant);
    175 
    176         markConstant(clazz, methodTypeConstant.u2descriptorIndex);
    177     }
    178 
    179 
    180     public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)
    181     {
    182         markAsUsed(nameAndTypeConstant);
    183 
    184         markConstant(clazz, nameAndTypeConstant.u2nameIndex);
    185         markConstant(clazz, nameAndTypeConstant.u2descriptorIndex);
    186     }
    187 
    188 
    189     // Implementations for AttributeVisitor.
    190 
    191     public void visitAnyAttribute(Clazz clazz, Attribute attribute)
    192     {
    193         markConstant(clazz, attribute.u2attributeNameIndex);
    194     }
    195 
    196 
    197     public void visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute)
    198     {
    199         markConstant(clazz, bootstrapMethodsAttribute.u2attributeNameIndex);
    200 
    201         // Mark the bootstrap method entries.
    202         bootstrapMethodsAttribute.bootstrapMethodEntriesAccept(clazz, this);
    203     }
    204 
    205 
    206     public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute)
    207     {
    208         markConstant(clazz, sourceFileAttribute.u2attributeNameIndex);
    209         markConstant(clazz, sourceFileAttribute.u2sourceFileIndex);
    210     }
    211 
    212 
    213     public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute)
    214     {
    215         markConstant(clazz, sourceDirAttribute.u2attributeNameIndex);
    216         markConstant(clazz, sourceDirAttribute.u2sourceDirIndex);
    217     }
    218 
    219 
    220     public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
    221     {
    222         markConstant(clazz, innerClassesAttribute.u2attributeNameIndex);
    223 
    224         // Mark the outer class entries.
    225         innerClassesAttribute.innerClassEntriesAccept(clazz, this);
    226     }
    227 
    228 
    229     public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
    230     {
    231         markConstant(clazz, enclosingMethodAttribute.u2attributeNameIndex);
    232         markConstant(clazz, enclosingMethodAttribute.u2classIndex);
    233 
    234         if (enclosingMethodAttribute.u2nameAndTypeIndex != 0)
    235         {
    236             markConstant(clazz, enclosingMethodAttribute.u2nameAndTypeIndex);
    237         }
    238     }
    239 
    240 
    241     public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute)
    242     {
    243         markConstant(clazz, signatureAttribute.u2attributeNameIndex);
    244         markConstant(clazz, signatureAttribute.u2signatureIndex);
    245     }
    246 
    247 
    248     public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute)
    249     {
    250         markConstant(clazz, constantValueAttribute.u2attributeNameIndex);
    251         markConstant(clazz, constantValueAttribute.u2constantValueIndex);
    252     }
    253 
    254 
    255     public void visitMethodParametersAttribute(Clazz clazz, Method method, MethodParametersAttribute methodParametersAttribute)
    256     {
    257         markConstant(clazz, methodParametersAttribute.u2attributeNameIndex);
    258 
    259         // Mark the constant pool entries referenced by the parameter information.
    260         methodParametersAttribute.parametersAccept(clazz, method, this);
    261     }
    262 
    263 
    264     public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute)
    265     {
    266         markConstant(clazz, exceptionsAttribute.u2attributeNameIndex);
    267 
    268         // Mark the constant pool entries referenced by the exceptions.
    269         exceptionsAttribute.exceptionEntriesAccept((ProgramClass)clazz, this);
    270     }
    271 
    272 
    273     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
    274     {
    275         markConstant(clazz, codeAttribute.u2attributeNameIndex);
    276 
    277         // Mark the constant pool entries referenced by the instructions,
    278         // by the exceptions, and by the attributes.
    279         codeAttribute.instructionsAccept(clazz, method, this);
    280         codeAttribute.exceptionsAccept(clazz, method, this);
    281         codeAttribute.attributesAccept(clazz, method, this);
    282     }
    283 
    284 
    285     public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute)
    286     {
    287         markConstant(clazz, stackMapAttribute.u2attributeNameIndex);
    288 
    289         // Mark the constant pool entries referenced by the stack map frames.
    290         stackMapAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
    291     }
    292 
    293 
    294     public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute)
    295     {
    296         markConstant(clazz, stackMapTableAttribute.u2attributeNameIndex);
    297 
    298         // Mark the constant pool entries referenced by the stack map frames.
    299         stackMapTableAttribute.stackMapFramesAccept(clazz, method, codeAttribute, this);
    300     }
    301 
    302 
    303     public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
    304     {
    305         markConstant(clazz, localVariableTableAttribute.u2attributeNameIndex);
    306 
    307         // Mark the constant pool entries referenced by the local variables.
    308         localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
    309     }
    310 
    311 
    312     public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
    313     {
    314         markConstant(clazz, localVariableTypeTableAttribute.u2attributeNameIndex);
    315 
    316         // Mark the constant pool entries referenced by the local variable types.
    317         localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this);
    318     }
    319 
    320 
    321     public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
    322     {
    323         markConstant(clazz, annotationsAttribute.u2attributeNameIndex);
    324 
    325         // Mark the constant pool entries referenced by the annotations.
    326         annotationsAttribute.annotationsAccept(clazz, this);
    327     }
    328 
    329 
    330     public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
    331     {
    332         markConstant(clazz, parameterAnnotationsAttribute.u2attributeNameIndex);
    333 
    334         // Mark the constant pool entries referenced by the annotations.
    335         parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
    336     }
    337 
    338 
    339     public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute)
    340     {
    341         markConstant(clazz, annotationDefaultAttribute.u2attributeNameIndex);
    342 
    343         // Mark the constant pool entries referenced by the element value.
    344         annotationDefaultAttribute.defaultValueAccept(clazz, this);
    345     }
    346 
    347 
    348     // Implementations for BootstrapMethodInfoVisitor.
    349 
    350     public void visitBootstrapMethodInfo(Clazz clazz, BootstrapMethodInfo bootstrapMethodInfo)
    351     {
    352         markConstant(clazz, bootstrapMethodInfo.u2methodHandleIndex);
    353 
    354         // Mark the constant pool entries referenced by the arguments.
    355         bootstrapMethodInfo.methodArgumentsAccept(clazz, this);
    356     }
    357 
    358 
    359     // Implementations for InnerClassesInfoVisitor.
    360 
    361     public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo)
    362     {
    363         innerClassesInfo.innerClassConstantAccept(clazz, this);
    364         innerClassesInfo.outerClassConstantAccept(clazz, this);
    365         innerClassesInfo.innerNameConstantAccept(clazz, this);
    366     }
    367 
    368 
    369     // Implementations for ExceptionInfoVisitor.
    370 
    371     public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
    372     {
    373         if (exceptionInfo.u2catchType != 0)
    374         {
    375             markConstant(clazz, exceptionInfo.u2catchType);
    376         }
    377     }
    378 
    379 
    380     // Implementations for StackMapFrameVisitor.
    381 
    382     public void visitAnyStackMapFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, StackMapFrame stackMapFrame) {}
    383 
    384 
    385     public void visitSameOneFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SameOneFrame sameOneFrame)
    386     {
    387         // Mark the constant pool entries referenced by the verification types.
    388         sameOneFrame.stackItemAccept(clazz, method, codeAttribute, offset, this);
    389     }
    390 
    391 
    392     public void visitMoreZeroFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, MoreZeroFrame moreZeroFrame)
    393     {
    394         // Mark the constant pool entries referenced by the verification types.
    395         moreZeroFrame.additionalVariablesAccept(clazz, method, codeAttribute, offset, this);
    396     }
    397 
    398 
    399     public void visitFullFrame(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, FullFrame fullFrame)
    400     {
    401         // Mark the constant pool entries referenced by the verification types.
    402         fullFrame.variablesAccept(clazz, method, codeAttribute, offset, this);
    403         fullFrame.stackAccept(clazz, method, codeAttribute, offset, this);
    404     }
    405 
    406 
    407     // Implementations for VerificationTypeVisitor.
    408 
    409     public void visitAnyVerificationType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VerificationType verificationType) {}
    410 
    411 
    412     public void visitObjectType(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ObjectType objectType)
    413     {
    414         markConstant(clazz, objectType.u2classIndex);
    415     }
    416 
    417 
    418     // Implementations for ParameterInfoVisitor.
    419 
    420     public void visitParameterInfo(Clazz clazz, Method method, int parameterIndex, ParameterInfo parameterInfo)
    421     {
    422         markConstant(clazz, parameterInfo.u2nameIndex);
    423     }
    424 
    425 
    426     // Implementations for LocalVariableInfoVisitor.
    427 
    428     public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo)
    429     {
    430         markConstant(clazz, localVariableInfo.u2nameIndex);
    431         markConstant(clazz, localVariableInfo.u2descriptorIndex);
    432     }
    433 
    434 
    435     // Implementations for LocalVariableTypeInfoVisitor.
    436 
    437     public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo)
    438     {
    439         markConstant(clazz, localVariableTypeInfo.u2nameIndex);
    440         markConstant(clazz, localVariableTypeInfo.u2signatureIndex);
    441     }
    442 
    443 
    444     // Implementations for AnnotationVisitor.
    445 
    446     public void visitAnnotation(Clazz clazz, Annotation annotation)
    447     {
    448         markConstant(clazz, annotation.u2typeIndex);
    449 
    450         // Mark the constant pool entries referenced by the element values.
    451         annotation.elementValuesAccept(clazz, this);
    452     }
    453 
    454 
    455     // Implementations for ElementValueVisitor.
    456 
    457     public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue)
    458     {
    459         if (constantElementValue.u2elementNameIndex != 0)
    460         {
    461             markConstant(clazz, constantElementValue.u2elementNameIndex);
    462         }
    463 
    464         markConstant(clazz, constantElementValue.u2constantValueIndex);
    465     }
    466 
    467 
    468     public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue)
    469     {
    470         if (enumConstantElementValue.u2elementNameIndex != 0)
    471         {
    472             markConstant(clazz, enumConstantElementValue.u2elementNameIndex);
    473         }
    474 
    475         markConstant(clazz, enumConstantElementValue.u2typeNameIndex);
    476         markConstant(clazz, enumConstantElementValue.u2constantNameIndex);
    477     }
    478 
    479 
    480     public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue)
    481     {
    482         if (classElementValue.u2elementNameIndex != 0)
    483         {
    484             markConstant(clazz, classElementValue.u2elementNameIndex);
    485         }
    486 
    487         markConstant(clazz, classElementValue.u2classInfoIndex);
    488     }
    489 
    490 
    491     public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
    492     {
    493         if (annotationElementValue.u2elementNameIndex != 0)
    494         {
    495             markConstant(clazz, annotationElementValue.u2elementNameIndex);
    496         }
    497 
    498         // Mark the constant pool entries referenced by the annotation.
    499         annotationElementValue.annotationAccept(clazz, this);
    500     }
    501 
    502 
    503     public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
    504     {
    505         if (arrayElementValue.u2elementNameIndex != 0)
    506         {
    507             markConstant(clazz, arrayElementValue.u2elementNameIndex);
    508         }
    509 
    510         // Mark the constant pool entries referenced by the element values.
    511         arrayElementValue.elementValuesAccept(clazz, annotation, this);
    512     }
    513 
    514 
    515     // Implementations for InstructionVisitor.
    516 
    517     public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
    518 
    519 
    520     public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
    521     {
    522         markConstant(clazz, constantInstruction.constantIndex);
    523     }
    524 
    525 
    526     // Small utility methods.
    527 
    528     /**
    529      * Marks the given constant pool entry of the given class. This includes
    530      * visiting any referenced objects.
    531      */
    532     private void markConstant(Clazz clazz, int index)
    533     {
    534         clazz.constantPoolEntryAccept(index, this);
    535     }
    536 
    537 
    538     /**
    539      * Marks the given visitor accepter as being used.
    540      */
    541     private void markAsUsed(Constant constant)
    542     {
    543         constant.setVisitorInfo(USED);
    544     }
    545 
    546 
    547     /**
    548      * Returns whether the given visitor accepter has been marked as being used.
    549      */
    550     private boolean isUsed(VisitorAccepter visitorAccepter)
    551     {
    552         return visitorAccepter.getVisitorInfo() == USED;
    553     }
    554 
    555 
    556     /**
    557      * Removes all constants that are not marked as being used from the given
    558      * constant pool.
    559      * @return the new number of entries.
    560      */
    561     private int shrinkConstantPool(Constant[] constantPool, int length)
    562     {
    563         // Create a new index map, if necessary.
    564         if (constantIndexMap.length < length)
    565         {
    566             constantIndexMap = new int[length];
    567         }
    568 
    569         int     counter = 1;
    570         boolean isUsed  = false;
    571 
    572         // Shift the used constant pool entries together.
    573         for (int index = 1; index < length; index++)
    574         {
    575             Constant constant = constantPool[index];
    576 
    577             // Is the constant being used? Don't update the flag if this is the
    578             // second half of a long entry.
    579             if (constant != null)
    580             {
    581                 isUsed = isUsed(constant);
    582             }
    583 
    584             if (isUsed)
    585             {
    586                 // Remember the new index.
    587                 constantIndexMap[index] = counter;
    588 
    589                 // Shift the constant pool entry.
    590                 constantPool[counter++] = constant;
    591             }
    592             else
    593             {
    594                 // Remember an invalid index.
    595                 constantIndexMap[index] = -1;
    596             }
    597         }
    598 
    599         // Clear the remaining constant pool elements.
    600         Arrays.fill(constantPool, counter, length, null);
    601 
    602         return counter;
    603     }
    604 }
    605