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 
     25 /**
     26  * This class can add interfaces and class members to a given class.
     27  * Elements to be added must be filled out beforehand, including their
     28  * references to the constant pool.
     29  *
     30  * @author Eric Lafortune
     31  */
     32 public class ClassEditor
     33 {
     34     private static final boolean DEBUG = false;
     35 
     36     private ProgramClass targetClass;
     37 
     38 
     39     /**
     40      * Creates a new ClassEditor that will edit elements in the given
     41      * target class.
     42      */
     43     public ClassEditor(ProgramClass targetClass)
     44     {
     45         this.targetClass = targetClass;
     46     }
     47 
     48 
     49     /**
     50      * Adds the given interface.
     51      */
     52     public void addInterface(int interfaceConstantIndex)
     53     {
     54         int   interfacesCount = targetClass.u2interfacesCount;
     55         int[] interfaces      = targetClass.u2interfaces;
     56 
     57         // Make sure there is enough space for the new interface.
     58         if (interfaces.length <= interfacesCount)
     59         {
     60             targetClass.u2interfaces = new int[interfacesCount+1];
     61             System.arraycopy(interfaces, 0,
     62                              targetClass.u2interfaces, 0,
     63                              interfacesCount);
     64             interfaces = targetClass.u2interfaces;
     65         }
     66 
     67         if (DEBUG)
     68         {
     69             System.out.println(targetClass.getName()+": adding interface ["+targetClass.getClassName(interfaceConstantIndex)+"]");
     70         }
     71 
     72         // Add the interface.
     73         interfaces[targetClass.u2interfacesCount++] = interfaceConstantIndex;
     74     }
     75 
     76     /**
     77      * Removes the given interface.
     78      */
     79     public void removeInterface(int interfaceConstantIndex)
     80     {
     81         int   interfacesCount = targetClass.u2interfacesCount;
     82         int[] interfaces      = targetClass.u2interfaces;
     83 
     84         int interfaceIndex = findInterfaceIndex(interfaceConstantIndex);
     85 
     86         // Shift the interface entries.
     87         System.arraycopy(interfaces, interfaceIndex+1,
     88                          interfaces, interfaceIndex,
     89                          interfacesCount - interfaceIndex - 1);
     90 
     91         // Clear the last entry.
     92         interfaces[--targetClass.u2interfacesCount] = 0;
     93     }
     94 
     95 
     96     /**
     97      * Finds the index of the given interface in the target class.
     98      */
     99 
    100     private int findInterfaceIndex(int interfaceConstantIndex)
    101     {
    102         int   interfacesCount = targetClass.u2interfacesCount;
    103         int[] interfaces      = targetClass.u2interfaces;
    104 
    105         for (int index = 0; index < interfacesCount; index++)
    106         {
    107             if (interfaces[index] == interfaceConstantIndex)
    108             {
    109                 return index;
    110             }
    111         }
    112 
    113         return interfacesCount;
    114     }
    115 
    116 
    117     /**
    118      * Adds the given field.
    119      */
    120     public void addField(Field field)
    121     {
    122         int     fieldsCount = targetClass.u2fieldsCount;
    123         Field[] fields      = targetClass.fields;
    124 
    125         // Make sure there is enough space for the new field.
    126         if (fields.length <= fieldsCount)
    127         {
    128             targetClass.fields = new ProgramField[fieldsCount+1];
    129             System.arraycopy(fields, 0,
    130                              targetClass.fields, 0,
    131                              fieldsCount);
    132             fields = targetClass.fields;
    133         }
    134 
    135         if (DEBUG)
    136         {
    137             System.out.println(targetClass.getName()+": adding field ["+field.getName(targetClass)+" "+field.getDescriptor(targetClass)+"]");
    138         }
    139 
    140         // Add the field.
    141         fields[targetClass.u2fieldsCount++] = field;
    142     }
    143 
    144 
    145     /**
    146      * Removes the given field. Note that removing a field that is still being
    147      * referenced can cause unpredictable effects.
    148      */
    149     public void removeField(Field field)
    150     {
    151         int     fieldsCount = targetClass.u2fieldsCount;
    152         Field[] fields      = targetClass.fields;
    153 
    154         int fieldIndex = findFieldIndex(field);
    155 
    156         // Shift the field entries.
    157         System.arraycopy(fields, fieldIndex+1,
    158                          fields, fieldIndex,
    159                          fieldsCount - fieldIndex - 1);
    160 
    161         // Clear the last entry.
    162         fields[--targetClass.u2fieldsCount] = null;
    163     }
    164 
    165 
    166     /**
    167      * Finds the index of the given field in the target class.
    168      */
    169 
    170     private int findFieldIndex(Field field)
    171     {
    172         int     fieldsCount = targetClass.u2fieldsCount;
    173         Field[] fields      = targetClass.fields;
    174 
    175         for (int index = 0; index < fieldsCount; index++)
    176         {
    177             if (fields[index].equals(field))
    178             {
    179                 return index;
    180             }
    181         }
    182 
    183         return fieldsCount;
    184     }
    185 
    186 
    187     /**
    188      * Adds the given method.
    189      */
    190     public void addMethod(Method method)
    191     {
    192         int      methodsCount = targetClass.u2methodsCount;
    193         Method[] methods      = targetClass.methods;
    194 
    195         // Make sure there is enough space for the new method.
    196         if (methods.length <= methodsCount)
    197         {
    198             targetClass.methods = new ProgramMethod[methodsCount+1];
    199             System.arraycopy(methods, 0,
    200                              targetClass.methods, 0,
    201                              methodsCount);
    202             methods = targetClass.methods;
    203         }
    204 
    205         if (DEBUG)
    206         {
    207             System.out.println(targetClass.getName()+": adding method ["+method.getName(targetClass)+method.getDescriptor(targetClass)+"]");
    208         }
    209 
    210         // Add the method.
    211         methods[targetClass.u2methodsCount++] = method;
    212     }
    213 
    214 
    215     /**
    216      * Removes the given method. Note that removing a method that is still being
    217      * referenced can cause unpredictable effects.
    218      */
    219     public void removeMethod(Method method)
    220     {
    221         int      methodsCount = targetClass.u2methodsCount;
    222         Method[] methods      = targetClass.methods;
    223 
    224         int methodIndex = findMethodIndex(method);
    225 
    226         // Shift the method entries.
    227         System.arraycopy(methods, methodIndex+1,
    228                          methods, methodIndex,
    229                          methodsCount - methodIndex - 1);
    230 
    231         // Clear the last entry.
    232         methods[--targetClass.u2methodsCount] = null;
    233     }
    234 
    235 
    236     /**
    237      * Finds the index of the given method in the target class.
    238      */
    239 
    240     private int findMethodIndex(Method method)
    241     {
    242         int      methodsCount = targetClass.u2methodsCount;
    243         Method[] methods      = targetClass.methods;
    244 
    245         for (int index = 0; index < methodsCount; index++)
    246         {
    247             if (methods[index].equals(method))
    248             {
    249                 return index;
    250             }
    251         }
    252 
    253         return methodsCount;
    254     }
    255 }
    256