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