/* * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package proguard.classfile.editor; import proguard.classfile.*; /** * This class can add interfaces and class members to a given class. * Elements to be added must be filled out beforehand, including their * references to the constant pool. * * @author Eric Lafortune */ public class ClassEditor { private static final boolean DEBUG = false; private ProgramClass targetClass; /** * Creates a new ClassEditor that will edit elements in the given * target class. */ public ClassEditor(ProgramClass targetClass) { this.targetClass = targetClass; } /** * Adds the given interface. */ public void addInterface(int interfaceConstantIndex) { int interfacesCount = targetClass.u2interfacesCount; int[] interfaces = targetClass.u2interfaces; // Make sure there is enough space for the new interface. if (interfaces.length <= interfacesCount) { targetClass.u2interfaces = new int[interfacesCount+1]; System.arraycopy(interfaces, 0, targetClass.u2interfaces, 0, interfacesCount); interfaces = targetClass.u2interfaces; } if (DEBUG) { System.out.println(targetClass.getName()+": adding interface ["+targetClass.getClassName(interfaceConstantIndex)+"]"); } // Add the interface. interfaces[targetClass.u2interfacesCount++] = interfaceConstantIndex; } /** * Removes the given interface. */ public void removeInterface(int interfaceConstantIndex) { int interfacesCount = targetClass.u2interfacesCount; int[] interfaces = targetClass.u2interfaces; int interfaceIndex = findInterfaceIndex(interfaceConstantIndex); // Shift the interface entries. System.arraycopy(interfaces, interfaceIndex+1, interfaces, interfaceIndex, interfacesCount - interfaceIndex - 1); // Clear the last entry. interfaces[--targetClass.u2interfacesCount] = 0; } /** * Finds the index of the given interface in the target class. */ private int findInterfaceIndex(int interfaceConstantIndex) { int interfacesCount = targetClass.u2interfacesCount; int[] interfaces = targetClass.u2interfaces; for (int index = 0; index < interfacesCount; index++) { if (interfaces[index] == interfaceConstantIndex) { return index; } } return interfacesCount; } /** * Adds the given field. */ public void addField(Field field) { int fieldsCount = targetClass.u2fieldsCount; Field[] fields = targetClass.fields; // Make sure there is enough space for the new field. if (fields.length <= fieldsCount) { targetClass.fields = new ProgramField[fieldsCount+1]; System.arraycopy(fields, 0, targetClass.fields, 0, fieldsCount); fields = targetClass.fields; } if (DEBUG) { System.out.println(targetClass.getName()+": adding field ["+field.getName(targetClass)+" "+field.getDescriptor(targetClass)+"]"); } // Add the field. fields[targetClass.u2fieldsCount++] = field; } /** * Removes the given field. Note that removing a field that is still being * referenced can cause unpredictable effects. */ public void removeField(Field field) { int fieldsCount = targetClass.u2fieldsCount; Field[] fields = targetClass.fields; int fieldIndex = findFieldIndex(field); // Shift the field entries. System.arraycopy(fields, fieldIndex+1, fields, fieldIndex, fieldsCount - fieldIndex - 1); // Clear the last entry. fields[--targetClass.u2fieldsCount] = null; } /** * Finds the index of the given field in the target class. */ private int findFieldIndex(Field field) { int fieldsCount = targetClass.u2fieldsCount; Field[] fields = targetClass.fields; for (int index = 0; index < fieldsCount; index++) { if (fields[index].equals(field)) { return index; } } return fieldsCount; } /** * Adds the given method. */ public void addMethod(Method method) { int methodsCount = targetClass.u2methodsCount; Method[] methods = targetClass.methods; // Make sure there is enough space for the new method. if (methods.length <= methodsCount) { targetClass.methods = new ProgramMethod[methodsCount+1]; System.arraycopy(methods, 0, targetClass.methods, 0, methodsCount); methods = targetClass.methods; } if (DEBUG) { System.out.println(targetClass.getName()+": adding method ["+method.getName(targetClass)+method.getDescriptor(targetClass)+"]"); } // Add the method. methods[targetClass.u2methodsCount++] = method; } /** * Removes the given method. Note that removing a method that is still being * referenced can cause unpredictable effects. */ public void removeMethod(Method method) { int methodsCount = targetClass.u2methodsCount; Method[] methods = targetClass.methods; int methodIndex = findMethodIndex(method); // Shift the method entries. System.arraycopy(methods, methodIndex+1, methods, methodIndex, methodsCount - methodIndex - 1); // Clear the last entry. methods[--targetClass.u2methodsCount] = null; } /** * Finds the index of the given method in the target class. */ private int findMethodIndex(Method method) { int methodsCount = targetClass.u2methodsCount; Method[] methods = targetClass.methods; for (int index = 0; index < methodsCount; index++) { if (methods[index].equals(method)) { return index; } } return methodsCount; } }