Home | History | Annotate | Download | only in optimize
      1 /*
      2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
      3  *             of Java bytecode.
      4  *
      5  * Copyright (c) 2002-2013 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;
     22 
     23 import proguard.classfile.*;
     24 import proguard.classfile.attribute.*;
     25 import proguard.classfile.attribute.annotation.*;
     26 import proguard.classfile.attribute.visitor.*;
     27 import proguard.classfile.editor.ConstantPoolEditor;
     28 import proguard.classfile.util.*;
     29 import proguard.classfile.visitor.MemberVisitor;
     30 import proguard.optimize.info.*;
     31 import proguard.optimize.peephole.VariableShrinker;
     32 
     33 /**
     34  * This MemberVisitor removes unused parameters in the descriptors of the
     35  * methods that it visits.
     36  *
     37  * @see ParameterUsageMarker
     38  * @see VariableUsageMarker
     39  * @see VariableShrinker
     40  * @author Eric Lafortune
     41  */
     42 public class MethodDescriptorShrinker
     43 extends      SimplifiedVisitor
     44 implements   MemberVisitor,
     45              AttributeVisitor
     46 {
     47     private static final boolean DEBUG = false;
     48 
     49 
     50     private final MemberVisitor extraMemberVisitor;
     51 
     52 
     53     /**
     54      * Creates a new MethodDescriptorShrinker.
     55      */
     56     public MethodDescriptorShrinker()
     57     {
     58         this(null);
     59     }
     60 
     61 
     62     /**
     63      * Creates a new MethodDescriptorShrinker with an extra visitor.
     64      * @param extraMemberVisitor an optional extra visitor for all methods whose
     65      *                           parameters have been simplified.
     66      */
     67     public MethodDescriptorShrinker(MemberVisitor extraMemberVisitor)
     68     {
     69         this.extraMemberVisitor = extraMemberVisitor;
     70     }
     71 
     72 
     73     // Implementations for MemberVisitor.
     74 
     75     public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)
     76     {
     77         // Update the descriptor if it has any unused parameters.
     78         String descriptor    = programMethod.getDescriptor(programClass);
     79         String newDescriptor = shrinkDescriptor(programMethod, descriptor);
     80 
     81         if (!descriptor.equals(newDescriptor))
     82         {
     83             // Shrink the signature and parameter annotations.
     84             programMethod.attributesAccept(programClass, this);
     85 
     86             String name    = programMethod.getName(programClass);
     87             String newName = name;
     88 
     89             // Append a code, if the method isn't a class instance initializer.
     90             if (!name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))
     91             {
     92                 newName += ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode()));
     93             }
     94 
     95             if (DEBUG)
     96             {
     97                 System.out.println("MethodDescriptorShrinker:");
     98                 System.out.println("  ["+programClass.getName()+"."+
     99                                    name+descriptor+"] -> ["+
    100                                    newName+newDescriptor+"]");
    101             }
    102 
    103             ConstantPoolEditor constantPoolEditor =
    104                 new ConstantPoolEditor(programClass);
    105 
    106             // Update the name, if necessary.
    107             if (!newName.equals(name))
    108             {
    109                 programMethod.u2nameIndex =
    110                     constantPoolEditor.addUtf8Constant(newName);
    111             }
    112 
    113             // Update the referenced classes.
    114             programMethod.referencedClasses =
    115                 shrinkReferencedClasses(programMethod,
    116                                         descriptor,
    117                                         programMethod.referencedClasses);
    118 
    119             // Finally, update the descriptor itself.
    120             programMethod.u2descriptorIndex =
    121                 constantPoolEditor.addUtf8Constant(newDescriptor);
    122 
    123             // Visit the method, if required.
    124             if (extraMemberVisitor != null)
    125             {
    126                 extraMemberVisitor.visitProgramMethod(programClass, programMethod);
    127             }
    128         }
    129     }
    130 
    131 
    132     // Implementations for AttributeVisitor.
    133 
    134     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
    135 
    136 
    137     public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute)
    138     {
    139         // Compute the new signature.
    140         String signature    = clazz.getString(signatureAttribute.u2signatureIndex);
    141         String newSignature = shrinkDescriptor(method, signature);
    142 
    143         // Update the signature.
    144         signatureAttribute.u2signatureIndex =
    145             new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature);
    146 
    147         // Update the referenced classes.
    148         signatureAttribute.referencedClasses =
    149             shrinkReferencedClasses(method,
    150                                     signature,
    151                                     signatureAttribute.referencedClasses);
    152     }
    153 
    154 
    155     public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute)
    156     {
    157         int[]          annotationsCounts = parameterAnnotationsAttribute.u2parameterAnnotationsCount;
    158         Annotation[][] annotations       = parameterAnnotationsAttribute.parameterAnnotations;
    159 
    160         // All parameters of non-static methods are shifted by one in the local
    161         // variable frame.
    162         int parameterIndex =
    163             (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ?
    164                 0 : 1;
    165 
    166         int annotationIndex    = 0;
    167         int newAnnotationIndex = 0;
    168 
    169         // Go over the parameters.
    170         String descriptor = method.getDescriptor(clazz);
    171         InternalTypeEnumeration internalTypeEnumeration =
    172             new InternalTypeEnumeration(descriptor);
    173 
    174         while (internalTypeEnumeration.hasMoreTypes())
    175         {
    176             String type = internalTypeEnumeration.nextType();
    177             if (ParameterUsageMarker.isParameterUsed(method, parameterIndex))
    178             {
    179                 annotationsCounts[newAnnotationIndex] = annotationsCounts[annotationIndex];
    180                 annotations[newAnnotationIndex++]     = annotations[annotationIndex];
    181             }
    182 
    183             annotationIndex++;
    184 
    185             parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
    186         }
    187 
    188         // Update the number of parameters.
    189         parameterAnnotationsAttribute.u2parametersCount = newAnnotationIndex;
    190 
    191         // Clear the unused entries.
    192         while (newAnnotationIndex < annotationIndex)
    193         {
    194             annotationsCounts[newAnnotationIndex] = 0;
    195             annotations[newAnnotationIndex++]     = null;
    196         }
    197     }
    198 
    199 
    200     // Small utility methods.
    201 
    202     /**
    203      * Returns a shrunk descriptor or signature of the given method.
    204      */
    205     private String shrinkDescriptor(Method method, String descriptor)
    206     {
    207         // All parameters of non-static methods are shifted by one in the local
    208         // variable frame.
    209         int parameterIndex =
    210             (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ?
    211                 0 : 1;
    212 
    213         // Go over the parameters.
    214         InternalTypeEnumeration internalTypeEnumeration =
    215             new InternalTypeEnumeration(descriptor);
    216 
    217         StringBuffer newDescriptorBuffer = new StringBuffer();
    218 
    219         newDescriptorBuffer.append(internalTypeEnumeration.formalTypeParameters());
    220         newDescriptorBuffer.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN);
    221 
    222         while (internalTypeEnumeration.hasMoreTypes())
    223         {
    224             String type = internalTypeEnumeration.nextType();
    225             if (ParameterUsageMarker.isParameterUsed(method, parameterIndex))
    226             {
    227                 newDescriptorBuffer.append(type);
    228             }
    229             else if (DEBUG)
    230             {
    231                 System.out.println("  Deleting parameter #"+parameterIndex+" ["+type+"]");
    232             }
    233 
    234             parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
    235         }
    236 
    237         newDescriptorBuffer.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE);
    238         newDescriptorBuffer.append(internalTypeEnumeration.returnType());
    239 
    240         return newDescriptorBuffer.toString();
    241     }
    242 
    243 
    244     /**
    245      * Shrinks the array of referenced classes of the given method.
    246      */
    247     private Clazz[] shrinkReferencedClasses(Method  method,
    248                                             String  descriptor,
    249                                             Clazz[] referencedClasses)
    250     {
    251         if (referencedClasses != null)
    252         {
    253             // All parameters of non-static methods are shifted by one in the local
    254             // variable frame.
    255             int parameterIndex =
    256                 (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ?
    257                     0 : 1;
    258 
    259             int referencedClassIndex    = 0;
    260             int newReferencedClassIndex = 0;
    261 
    262             // Go over the parameters.
    263             InternalTypeEnumeration internalTypeEnumeration =
    264                 new InternalTypeEnumeration(descriptor);
    265 
    266             // Also look at the formal type parameters.
    267             String type  = internalTypeEnumeration.formalTypeParameters();
    268             int    count = new DescriptorClassEnumeration(type).classCount();
    269             for (int counter = 0; counter < count; counter++)
    270             {
    271                 referencedClasses[newReferencedClassIndex++] =
    272                     referencedClasses[referencedClassIndex++];
    273             }
    274 
    275             while (internalTypeEnumeration.hasMoreTypes())
    276             {
    277                 // Consider the classes referenced by this parameter type.
    278                 type  = internalTypeEnumeration.nextType();
    279                 count = new DescriptorClassEnumeration(type).classCount();
    280 
    281                 if (ParameterUsageMarker.isParameterUsed(method, parameterIndex))
    282                 {
    283                     // Copy the referenced classes.
    284                     for (int counter = 0; counter < count; counter++)
    285                     {
    286                         referencedClasses[newReferencedClassIndex++] =
    287                             referencedClasses[referencedClassIndex++];
    288                     }
    289                 }
    290                 else
    291                 {
    292                     // Skip the referenced classes.
    293                     referencedClassIndex += count;
    294                 }
    295 
    296                 parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
    297             }
    298 
    299             // Also look at the return value.
    300             type  = internalTypeEnumeration.returnType();
    301             count = new DescriptorClassEnumeration(type).classCount();
    302             for (int counter = 0; counter < count; counter++)
    303             {
    304                 referencedClasses[newReferencedClassIndex++] =
    305                     referencedClasses[referencedClassIndex++];
    306             }
    307 
    308             // Clear the unused entries.
    309             while (newReferencedClassIndex < referencedClassIndex)
    310             {
    311                 referencedClasses[newReferencedClassIndex++] = null;
    312             }
    313         }
    314 
    315         return referencedClasses;
    316     }
    317 }
    318