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.visitor.AttributeVisitor;
     26 import proguard.classfile.util.SimplifiedVisitor;
     27 
     28 import java.util.Arrays;
     29 
     30 /**
     31  * This AttributeVisitor cleans up variable tables in all code attributes that
     32  * it visits. It trims overlapping local variables. It removes empty local
     33  * variables and empty local variable tables.
     34  *
     35  * @author Eric Lafortune
     36  */
     37 public class VariableCleaner
     38 extends      SimplifiedVisitor
     39 implements   AttributeVisitor
     40 {
     41     private boolean deleteLocalVariableTableAttribute;
     42     private boolean deleteLocalVariableTypeTableAttribute;
     43 
     44 
     45     // Implementations for AttributeVisitor.
     46 
     47     public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
     48 
     49 
     50     public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
     51     {
     52         deleteLocalVariableTableAttribute     = false;
     53         deleteLocalVariableTypeTableAttribute = false;
     54 
     55         // Trim the local variable table and the local variable type table.
     56         codeAttribute.attributesAccept(clazz, method, this);
     57 
     58         // Delete the local variable table if it ended up empty.
     59         if (deleteLocalVariableTableAttribute)
     60         {
     61             AttributesEditor editor =
     62                 new AttributesEditor((ProgramClass)clazz,
     63                                      (ProgramMember)method,
     64                                      codeAttribute,
     65                                      true);
     66 
     67             editor.deleteAttribute(ClassConstants.ATTR_LocalVariableTable);
     68         }
     69 
     70         // Delete the local variable type table if it ended up empty.
     71         if (deleteLocalVariableTypeTableAttribute)
     72         {
     73             AttributesEditor editor =
     74                 new AttributesEditor((ProgramClass)clazz,
     75                                      (ProgramMember)method,
     76                                      codeAttribute,
     77                                      true);
     78 
     79             editor.deleteAttribute(ClassConstants.ATTR_LocalVariableTypeTable);
     80         }
     81     }
     82 
     83 
     84     public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute)
     85     {
     86         // Clean up local variables that aren't used.
     87         localVariableTableAttribute.u2localVariableTableLength =
     88             removeUnusedLocalVariables(localVariableTableAttribute.localVariableTable,
     89                                        localVariableTableAttribute.u2localVariableTableLength,
     90                                        codeAttribute.u2maxLocals);
     91 
     92         // Trim the code blocks of the local variables.
     93         trimLocalVariables(localVariableTableAttribute.localVariableTable,
     94                            localVariableTableAttribute.u2localVariableTableLength,
     95                            codeAttribute.u2maxLocals);
     96 
     97         // Delete the attribute in a moment, if it is empty.
     98         if (localVariableTableAttribute.u2localVariableTableLength == 0)
     99         {
    100             deleteLocalVariableTableAttribute = true;
    101         }
    102     }
    103 
    104 
    105     public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute)
    106     {
    107         // Clean up local variables that aren't used.
    108         localVariableTypeTableAttribute.u2localVariableTypeTableLength =
    109             removeUnusedLocalVariableTypes(localVariableTypeTableAttribute.localVariableTypeTable,
    110                                            localVariableTypeTableAttribute.u2localVariableTypeTableLength,
    111                                            codeAttribute.u2maxLocals);
    112 
    113         // Trim the code blocks of the local variables.
    114         trimLocalVariableTypes(localVariableTypeTableAttribute.localVariableTypeTable,
    115                                localVariableTypeTableAttribute.u2localVariableTypeTableLength,
    116                                codeAttribute.u2maxLocals);
    117 
    118         // Delete the attribute in a moment, if it is empty.
    119         if (localVariableTypeTableAttribute.u2localVariableTypeTableLength == 0)
    120         {
    121             deleteLocalVariableTypeTableAttribute = true;
    122         }
    123     }
    124 
    125 
    126     // Small utility methods.
    127 
    128     /**
    129      * Returns the given list of local variables, without the ones that aren't
    130      * used.
    131      */
    132     private int removeUnusedLocalVariables(LocalVariableInfo[] localVariableInfos,
    133                                            int                 localVariableInfoCount,
    134                                            int                 maxLocals)
    135     {
    136         // Overwrite all empty local variable entries.
    137         // Do keep parameter entries.
    138         int newIndex = 0;
    139         for (int index = 0; index < localVariableInfoCount; index++)
    140         {
    141             LocalVariableInfo localVariableInfo = localVariableInfos[index];
    142 
    143             if (localVariableInfo.u2index >= 0        &&
    144                 localVariableInfo.u2index < maxLocals &&
    145                 (localVariableInfo.u2startPC == 0 ||
    146                  localVariableInfo.u2length > 0))
    147             {
    148                 localVariableInfos[newIndex++] = localVariableInfos[index];
    149             }
    150         }
    151 
    152         // Clean up any remaining array elements.
    153         Arrays.fill(localVariableInfos, newIndex, localVariableInfoCount, null);
    154 
    155         return newIndex;
    156     }
    157 
    158 
    159     /**
    160      * Returns the given list of local variable types, without the ones that
    161      * aren't used.
    162      */
    163     private int removeUnusedLocalVariableTypes(LocalVariableTypeInfo[] localVariableTypeInfos,
    164                                                int                     localVariableTypeInfoCount,
    165                                                int                     maxLocals)
    166     {
    167         // Overwrite all empty local variable type entries.
    168         // Do keep parameter entries.
    169         int newIndex = 0;
    170         for (int index = 0; index < localVariableTypeInfoCount; index++)
    171         {
    172             LocalVariableTypeInfo localVariableTypeInfo = localVariableTypeInfos[index];
    173 
    174             if (localVariableTypeInfo.u2index >= 0        &&
    175                 localVariableTypeInfo.u2index < maxLocals &&
    176                 (localVariableTypeInfo.u2startPC == 0 ||
    177                  localVariableTypeInfo.u2length > 0))
    178             {
    179                 localVariableTypeInfos[newIndex++] = localVariableTypeInfos[index];
    180             }
    181         }
    182 
    183         // Clean up any remaining array elements.
    184         Arrays.fill(localVariableTypeInfos,  newIndex, localVariableTypeInfoCount, null);
    185 
    186         return newIndex;
    187     }
    188 
    189 
    190     /**
    191      * Sorts the given list of local variables and trims their code blocks
    192      * when necessary. The block is trimmed at the end, which is a bit
    193      * arbitrary.
    194      */
    195     private void trimLocalVariables(LocalVariableInfo[] localVariableInfos,
    196                                     int                 localVariableInfoCount,
    197                                     int                 maxLocals)
    198     {
    199         // Sort the local variable entries.
    200         Arrays.sort(localVariableInfos, 0, localVariableInfoCount);
    201 
    202         int[] startPCs = createMaxArray(maxLocals);
    203 
    204         // Trim the local variable entries, starting at the last one.
    205         for (int index = localVariableInfoCount-1; index >= 0; index--)
    206         {
    207             LocalVariableInfo localVariableInfo = localVariableInfos[index];
    208 
    209             // Make sure the variable's code block doesn't overlap with the
    210             // next one for the same variable.
    211             int maxLength = startPCs[localVariableInfo.u2index] -
    212                             localVariableInfo.u2startPC;
    213 
    214             if (localVariableInfo.u2length > maxLength)
    215             {
    216                 localVariableInfo.u2length = maxLength;
    217             }
    218 
    219             startPCs[localVariableInfo.u2index] = localVariableInfo.u2startPC;
    220         }
    221     }
    222 
    223 
    224     /**
    225      * Sorts the given list of local variable types and trims their code blocks
    226      * when necessary. The block is trimmed at the end, which is a bit
    227      * arbitrary.
    228      */
    229     private void trimLocalVariableTypes(LocalVariableTypeInfo[] localVariableTypeInfos,
    230                                         int                     localVariableTypeInfoCount,
    231                                         int                     maxLocals)
    232     {
    233         // Sort the local variable entries.
    234         Arrays.sort(localVariableTypeInfos, 0, localVariableTypeInfoCount);
    235 
    236         int[] startPCs = createMaxArray(maxLocals);
    237 
    238         // Trim the local variable entries, starting at the last one.
    239         for (int index = localVariableTypeInfoCount-1; index >= 0; index--)
    240         {
    241             LocalVariableTypeInfo localVariableTypeInfo = localVariableTypeInfos[index];
    242 
    243             // Make sure the variable's code block doesn't overlap with the
    244             // next one for the same variable.
    245             int maxLength = startPCs[localVariableTypeInfo.u2index] -
    246                             localVariableTypeInfo.u2startPC;
    247 
    248             if (localVariableTypeInfo.u2length > maxLength)
    249             {
    250                 localVariableTypeInfo.u2length = maxLength;
    251             }
    252 
    253             startPCs[localVariableTypeInfo.u2index] = localVariableTypeInfo.u2startPC;
    254         }
    255     }
    256 
    257 
    258     /**
    259      * Creates an integer array of the given length, initialized with
    260      * Integer.MAX_VALUE.
    261      */
    262     private int[] createMaxArray(int length)
    263     {
    264         int[] startPCs = new int[length];
    265         for (int index = 0; index < length; index++)
    266         {
    267             startPCs[index] = Integer.MAX_VALUE;
    268         }
    269         return startPCs;
    270     }
    271 }