Home | History | Annotate | Download | only in shrink
      1 /*
      2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
      3  *             of Java bytecode.
      4  *
      5  * Copyright (c) 2002-2009 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.shrink;
     22 
     23 import proguard.classfile.*;
     24 import proguard.classfile.attribute.*;
     25 import proguard.classfile.attribute.annotation.*;
     26 import proguard.classfile.attribute.annotation.visitor.*;
     27 import proguard.classfile.attribute.visitor.AttributeVisitor;
     28 import proguard.classfile.constant.*;
     29 import proguard.classfile.editor.*;
     30 import proguard.classfile.util.*;
     31 import proguard.classfile.visitor.*;
     32 
     33 /**
     34  * This ClassVisitor removes constant pool entries and class members that
     35  * are not marked as being used.
     36  *
     37  * @see UsageMarker
     38  *
     39  * @author Eric Lafortune
     40  */
     41 public class ClassShrinker
     42 extends      SimplifiedVisitor
     43 implements   ClassVisitor,
     44              MemberVisitor,
     45              AttributeVisitor,
     46              AnnotationVisitor,
     47              ElementValueVisitor
     48 {
     49     private final UsageMarker usageMarker;
     50 
     51     private int[] constantIndexMap = new int[ClassConstants.TYPICAL_CONSTANT_POOL_SIZE];
     52 
     53     private final ConstantPoolRemapper constantPoolRemapper = new ConstantPoolRemapper();
     54 
     55 
     56     /**
     57      * Creates a new ClassShrinker.
     58      * @param usageMarker the usage marker that is used to mark the classes
     59      *                    and class members.
     60      */
     61     public ClassShrinker(UsageMarker usageMarker)
     62     {
     63         this.usageMarker = usageMarker;
     64     }
     65 
     66 
     67     // Implementations for ClassVisitor.
     68 
     69     public void visitProgramClass(ProgramClass programClass)
     70     {
     71         // Shrink the arrays for constant pool, interfaces, fields, methods,
     72         // and class attributes.
     73         programClass.u2interfacesCount =
     74             shrinkConstantIndexArray(programClass.constantPool,
     75                                      programClass.u2interfaces,
     76                                      programClass.u2interfacesCount);
     77 
     78         // Shrinking the constant pool also sets up an index map.
     79         programClass.u2constantPoolCount =
     80             shrinkConstantPool(programClass.constantPool,
     81                                programClass.u2constantPoolCount);
     82 
     83         programClass.u2fieldsCount =
     84             shrinkArray(programClass.fields,
     85                         programClass.u2fieldsCount);
     86 
     87         programClass.u2methodsCount =
     88             shrinkArray(programClass.methods,
     89                         programClass.u2methodsCount);
     90 
     91         programClass.u2attributesCount =
     92             shrinkArray(programClass.attributes,
     93                         programClass.u2attributesCount);
     94 
     95         // Compact the remaining fields, methods, and attributes,
     96         // and remap their references to the constant pool.
     97         programClass.fieldsAccept(this);
     98         programClass.methodsAccept(this);
     99         programClass.attributesAccept(this);
    100 
    101         // Remap all constant pool references.
    102         constantPoolRemapper.setConstantIndexMap(constantIndexMap);
    103         constantPoolRemapper.visitProgramClass(programClass);
    104 
    105         // Remove the unused interfaces from the class signature.
    106         programClass.attributesAccept(new SignatureShrinker());
    107 
    108         // Compact the extra field pointing to the subclasses of this class.
    109         programClass.subClasses =
    110             shrinkToNewArray(programClass.subClasses);
    111     }
    112 
    113 
    114     public void visitLibraryClass(LibraryClass libraryClass)
    115     {
    116         // Library classes are left unchanged.
    117 
    118         // Compact the extra field pointing to the subclasses of this class.
    119         libraryClass.subClasses =
    120             shrinkToNewArray(libraryClass.subClasses);
    121     }
    122 
    123 
    124     // Implementations for MemberVisitor.
    125 
    126     public void visitProgramMember(ProgramClass programClass, ProgramMember programMember)
    127     {
    128         // Shrink the attributes array.
    129         programMember.u2attributesCount =
    130             shrinkArray(programMember.attributes,
    131                         programMember.u2attributesCount);
    132 
    133         // Shrink any attributes.
    134         programMember.attributesAccept(programClass, this);
    135     }
    136 
    137 
    138     // Implementations for AttributeVisitor.
    139 
    140     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
    141 
    142 
    143     public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute)
    144     {
    145         // Shrink the array of InnerClassesInfo objects.
    146         innerClassesAttribute.u2classesCount =
    147             shrinkArray(innerClassesAttribute.classes,
    148                         innerClassesAttribute.u2classesCount);
    149     }
    150 
    151 
    152     public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute)
    153     {
    154         // Sometimes, a class is still referenced (apparently as a dummy class),
    155         // but its enclosing method is not. Then remove the reference to
    156         // the enclosing method.
    157         // E.g. the anonymous inner class javax.swing.JList$1 is defined inside
    158         // a constructor of javax.swing.JList, but it is also referenced as a
    159         // dummy argument in a constructor of javax.swing.JList$ListSelectionHandler.
    160         if (enclosingMethodAttribute.referencedMethod != null &&
    161             !usageMarker.isUsed(enclosingMethodAttribute.referencedMethod))
    162         {
    163             enclosingMethodAttribute.u2nameAndTypeIndex = 0;
    164 
    165             enclosingMethodAttribute.referencedMethod = null;
    166         }
    167     }
    168 
    169 
    170     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
    171     {
    172         // Shrink the attributes array.
    173         codeAttribute.u2attributesCount =
    174             shrinkArray(codeAttribute.attributes,
    175                         codeAttribute.u2attributesCount);
    176     }
    177 
    178 
    179     public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute)
    180     {
    181         // Shrink the annotations array.
    182         annotationsAttribute.u2annotationsCount =
    183             shrinkArray(annotationsAttribute.annotations,
    184                         annotationsAttribute.u2annotationsCount);
    185 
    186         // Shrink the annotations themselves.
    187         annotationsAttribute.annotationsAccept(clazz, this);
    188     }
    189 
    190 
    191     public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
    192     {
    193         // Loop over all parameters.
    194         for (int parameterIndex = 0; parameterIndex < parameterAnnotationsAttribute.u2parametersCount; parameterIndex++)
    195         {
    196             // Shrink the parameter annotations array.
    197             parameterAnnotationsAttribute.u2parameterAnnotationsCount[parameterIndex] =
    198                 shrinkArray(parameterAnnotationsAttribute.parameterAnnotations[parameterIndex],
    199                             parameterAnnotationsAttribute.u2parameterAnnotationsCount[parameterIndex]);
    200         }
    201 
    202         // Shrink the annotations themselves.
    203         parameterAnnotationsAttribute.annotationsAccept(clazz, method, this);
    204     }
    205 
    206 
    207     // Implementations for AnnotationVisitor.
    208 
    209     public void visitAnnotation(Clazz clazz, Annotation annotation)
    210     {
    211         // Shrink the element values array.
    212         annotation.u2elementValuesCount =
    213             shrinkArray(annotation.elementValues,
    214                         annotation.u2elementValuesCount);
    215 
    216         // Shrink the element values themselves.
    217         annotation.elementValuesAccept(clazz, this);
    218     }
    219 
    220 
    221     /**
    222      * This AttributeVisitor updates the Utf8 constants of class signatures,
    223      * removing any unused interfaces.
    224      */
    225     private class SignatureShrinker
    226     extends       SimplifiedVisitor
    227     implements    AttributeVisitor
    228     {
    229         public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
    230 
    231 
    232         public void visitSignatureAttribute(Clazz clazz, SignatureAttribute  signatureAttribute)
    233         {
    234             Clazz[] referencedClasses = signatureAttribute.referencedClasses;
    235             if (referencedClasses != null)
    236             {
    237                 // Go over the generic definitions, superclass and implemented interfaces.
    238                 String signature = clazz.getString(signatureAttribute.u2signatureIndex);
    239 
    240                 InternalTypeEnumeration internalTypeEnumeration =
    241                     new InternalTypeEnumeration(signature);
    242 
    243                 StringBuffer newSignatureBuffer = new StringBuffer();
    244 
    245                 int referencedClassIndex    = 0;
    246                 int newReferencedClassIndex = 0;
    247 
    248                 while (internalTypeEnumeration.hasMoreTypes())
    249                 {
    250                     // Consider the classes referenced by this signature.
    251                     String type       = internalTypeEnumeration.nextType();
    252                     int    classCount = new DescriptorClassEnumeration(type).classCount();
    253 
    254                     Clazz referencedClass = referencedClasses[referencedClassIndex];
    255                     if (referencedClass == null ||
    256                         usageMarker.isUsed(referencedClass))
    257                     {
    258                         // Append the superclass or interface.
    259                         newSignatureBuffer.append(type);
    260 
    261                         // Copy the referenced classes.
    262                         for (int counter = 0; counter < classCount; counter++)
    263                         {
    264                             referencedClasses[newReferencedClassIndex++] =
    265                                 referencedClasses[referencedClassIndex++];
    266                         }
    267                     }
    268                     else
    269                     {
    270                         // Skip the referenced classes.
    271                         referencedClassIndex += classCount;
    272                     }
    273                 }
    274 
    275                 if (newReferencedClassIndex < referencedClassIndex)
    276                 {
    277                     // Update the signature.
    278                     ((Utf8Constant)((ProgramClass)clazz).constantPool[signatureAttribute.u2signatureIndex]).setString(newSignatureBuffer.toString());
    279 
    280                     // Clear the unused entries.
    281                     while (newReferencedClassIndex < referencedClassIndex)
    282                     {
    283                         referencedClasses[newReferencedClassIndex++] = null;
    284                     }
    285                 }
    286             }
    287         }
    288     }
    289 
    290 
    291     // Implementations for ElementValueVisitor.
    292 
    293     public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue) {}
    294 
    295 
    296     public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue)
    297     {
    298         // Shrink the contained annotation.
    299         annotationElementValue.annotationAccept(clazz, this);
    300     }
    301 
    302 
    303     public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue)
    304     {
    305         // Shrink the element values array.
    306         arrayElementValue.u2elementValuesCount =
    307             shrinkArray(arrayElementValue.elementValues,
    308                         arrayElementValue.u2elementValuesCount);
    309 
    310         // Shrink the element values themselves.
    311         arrayElementValue.elementValuesAccept(clazz, annotation, this);
    312     }
    313 
    314 
    315     // Small utility methods.
    316 
    317     /**
    318      * Removes all entries that are not marked as being used from the given
    319      * constant pool.
    320      * @return the new number of entries.
    321      */
    322     private int shrinkConstantPool(Constant[] constantPool, int length)
    323     {
    324         if (constantIndexMap.length < length)
    325         {
    326             constantIndexMap = new int[length];
    327         }
    328 
    329         int     counter = 1;
    330         boolean isUsed  = false;
    331 
    332         // Shift the used constant pool entries together.
    333         for (int index = 1; index < length; index++)
    334         {
    335             constantIndexMap[index] = counter;
    336 
    337             Constant constant = constantPool[index];
    338 
    339             // Don't update the flag if this is the second half of a long entry.
    340             if (constant != null)
    341             {
    342                 isUsed = usageMarker.isUsed(constant);
    343             }
    344 
    345             if (isUsed)
    346             {
    347                 constantPool[counter++] = constant;
    348             }
    349         }
    350 
    351         // Clear the remaining constant pool elements.
    352         for (int index = counter; index < length; index++)
    353         {
    354             constantPool[index] = null;
    355         }
    356 
    357         return counter;
    358     }
    359 
    360 
    361     /**
    362      * Removes all indices that point to unused constant pool entries
    363      * from the given array.
    364      * @return the new number of indices.
    365      */
    366     private int shrinkConstantIndexArray(Constant[] constantPool, int[] array, int length)
    367     {
    368         int counter = 0;
    369 
    370         // Shift the used objects together.
    371         for (int index = 0; index < length; index++)
    372         {
    373             if (usageMarker.isUsed(constantPool[array[index]]))
    374             {
    375                 array[counter++] = array[index];
    376             }
    377         }
    378 
    379         // Clear the remaining array elements.
    380         for (int index = counter; index < length; index++)
    381         {
    382             array[index] = 0;
    383         }
    384 
    385         return counter;
    386     }
    387 
    388 
    389     /**
    390      * Removes all Clazz objects that are not marked as being used
    391      * from the given array and returns the remaining objects in a an array
    392      * of the right size.
    393      * @return the new array.
    394      */
    395     private Clazz[] shrinkToNewArray(Clazz[] array)
    396     {
    397         if (array == null)
    398         {
    399             return null;
    400         }
    401 
    402         // Shrink the given array in-place.
    403         int length = shrinkArray(array, array.length);
    404         if (length == 0)
    405         {
    406             return null;
    407         }
    408 
    409         // Return immediately if the array is of right size already.
    410         if (length == array.length)
    411         {
    412             return array;
    413         }
    414 
    415         // Copy the remaining elements into a new array of the right size.
    416         Clazz[] newArray = new Clazz[length];
    417         System.arraycopy(array, 0, newArray, 0, length);
    418         return newArray;
    419     }
    420 
    421 
    422     /**
    423      * Removes all VisitorAccepter objects that are not marked as being used
    424      * from the given array.
    425      * @return the new number of VisitorAccepter objects.
    426      */
    427     private int shrinkArray(VisitorAccepter[] array, int length)
    428     {
    429         int counter = 0;
    430 
    431         // Shift the used objects together.
    432         for (int index = 0; index < length; index++)
    433         {
    434             if (usageMarker.isUsed(array[index]))
    435             {
    436                 array[counter++] = array[index];
    437             }
    438         }
    439 
    440         // Clear the remaining array elements.
    441         for (int index = counter; index < length; index++)
    442         {
    443             array[index] = null;
    444         }
    445 
    446         return counter;
    447     }
    448 }
    449