/* * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * * Copyright (c) 2002-2014 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.optimize; import proguard.classfile.*; import proguard.classfile.attribute.*; import proguard.classfile.attribute.annotation.*; import proguard.classfile.attribute.visitor.AttributeVisitor; import proguard.classfile.editor.ConstantPoolEditor; import proguard.classfile.util.*; import proguard.classfile.visitor.MemberVisitor; import proguard.optimize.info.*; import proguard.optimize.peephole.VariableShrinker; import java.util.Arrays; /** * This MemberVisitor removes unused parameters in the descriptors of the * methods that it visits. * * @see ParameterUsageMarker * @see VariableUsageMarker * @see VariableShrinker * @author Eric Lafortune */ public class MethodDescriptorShrinker extends SimplifiedVisitor implements MemberVisitor, AttributeVisitor { private static final boolean DEBUG = false; private final MemberVisitor extraMemberVisitor; /** * Creates a new MethodDescriptorShrinker. */ public MethodDescriptorShrinker() { this(null); } /** * Creates a new MethodDescriptorShrinker with an extra visitor. * @param extraMemberVisitor an optional extra visitor for all methods whose * parameters have been simplified. */ public MethodDescriptorShrinker(MemberVisitor extraMemberVisitor) { this.extraMemberVisitor = extraMemberVisitor; } // Implementations for MemberVisitor. public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) { if (DEBUG) { System.out.println("MethodDescriptorShrinker: ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"]"); } // Update the descriptor if it has any unused parameters. String descriptor = programMethod.getDescriptor(programClass); String newDescriptor = shrinkDescriptor(programMethod, descriptor); if (!newDescriptor.equals(descriptor)) { String name = programMethod.getName(programClass); String newName = name; // Append a code, if the method isn't a class instance initializer. if (!name.equals(ClassConstants.METHOD_NAME_INIT)) { newName += ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode())); } ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor(programClass); // Update the name, if necessary. if (!newName.equals(name)) { programMethod.u2nameIndex = constantPoolEditor.addUtf8Constant(newName); } // Update the referenced classes. programMethod.referencedClasses = shrinkReferencedClasses(programMethod, descriptor, programMethod.referencedClasses); // Finally, update the descriptor itself. programMethod.u2descriptorIndex = constantPoolEditor.addUtf8Constant(newDescriptor); if (DEBUG) { System.out.println(" -> ["+newName+newDescriptor+"]"); } // Shrink the signature and parameter annotations. programMethod.attributesAccept(programClass, this); // Visit the method, if required. if (extraMemberVisitor != null) { extraMemberVisitor.visitProgramMethod(programClass, programMethod); } } } // Implementations for AttributeVisitor. public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute) { if (DEBUG) { System.out.println(" ["+signatureAttribute.getSignature(clazz)+"]"); } // Compute the new signature. String signature = signatureAttribute.getSignature(clazz); String newSignature = shrinkDescriptor(method, signature); if (!newSignature.equals(signature)) { // Update the signature. signatureAttribute.u2signatureIndex = new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature); // Update the referenced classes. signatureAttribute.referencedClasses = shrinkReferencedClasses(method, signature, signatureAttribute.referencedClasses); if (DEBUG) { System.out.println(" -> ["+newSignature+"]"); } } } public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) { int[] annotationsCounts = parameterAnnotationsAttribute.u2parameterAnnotationsCount; Annotation[][] annotations = parameterAnnotationsAttribute.parameterAnnotations; // All parameters of non-static methods are shifted by one in the local // variable frame. int parameterIndex = (method.getAccessFlags() & ClassConstants.ACC_STATIC) != 0 ? 0 : 1; int annotationIndex = 0; int newAnnotationIndex = 0; // Go over the parameters. String descriptor = method.getDescriptor(clazz); InternalTypeEnumeration internalTypeEnumeration = new InternalTypeEnumeration(descriptor); while (internalTypeEnumeration.hasMoreTypes()) { String type = internalTypeEnumeration.nextType(); if (ParameterUsageMarker.isParameterUsed(method, parameterIndex)) { annotationsCounts[newAnnotationIndex] = annotationsCounts[annotationIndex]; annotations[newAnnotationIndex++] = annotations[annotationIndex]; } annotationIndex++; parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1; } // Update the number of parameters. parameterAnnotationsAttribute.u1parametersCount = newAnnotationIndex; // Clear the unused entries. while (newAnnotationIndex < annotationIndex) { annotationsCounts[newAnnotationIndex] = 0; annotations[newAnnotationIndex++] = null; } } // Small utility methods. /** * Returns a shrunk descriptor or signature of the given method. */ private String shrinkDescriptor(Method method, String descriptor) { // All parameters of non-static methods are shifted by one in the local // variable frame. int parameterIndex = (method.getAccessFlags() & ClassConstants.ACC_STATIC) != 0 ? 0 : 1; InternalTypeEnumeration internalTypeEnumeration = new InternalTypeEnumeration(descriptor); StringBuffer newDescriptorBuffer = new StringBuffer(descriptor.length()); // Copy the formal type parameters. newDescriptorBuffer.append(internalTypeEnumeration.formalTypeParameters()); newDescriptorBuffer.append(ClassConstants.METHOD_ARGUMENTS_OPEN); // Go over the parameters. while (internalTypeEnumeration.hasMoreTypes()) { String type = internalTypeEnumeration.nextType(); if (ParameterUsageMarker.isParameterUsed(method, parameterIndex)) { newDescriptorBuffer.append(type); } else if (DEBUG) { System.out.println(" Deleting parameter #"+parameterIndex+" ["+type+"]"); } parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1; } // Copy the return type. newDescriptorBuffer.append(ClassConstants.METHOD_ARGUMENTS_CLOSE); newDescriptorBuffer.append(internalTypeEnumeration.returnType()); return newDescriptorBuffer.toString(); } /** * Shrinks the array of referenced classes of the given method. */ private Clazz[] shrinkReferencedClasses(Method method, String descriptor, Clazz[] referencedClasses) { if (referencedClasses != null) { // All parameters of non-static methods are shifted by one in the local // variable frame. int parameterIndex = (method.getAccessFlags() & ClassConstants.ACC_STATIC) != 0 ? 0 : 1; InternalTypeEnumeration internalTypeEnumeration = new InternalTypeEnumeration(descriptor); int referencedClassIndex = 0; int newReferencedClassIndex = 0; // Copy the formal type parameters. { String type = internalTypeEnumeration.formalTypeParameters(); int count = new DescriptorClassEnumeration(type).classCount(); for (int counter = 0; counter < count; counter++) { referencedClasses[newReferencedClassIndex++] = referencedClasses[referencedClassIndex++]; } } // Go over the parameters. while (internalTypeEnumeration.hasMoreTypes()) { // Consider the classes referenced by this parameter type. String type = internalTypeEnumeration.nextType(); int count = new DescriptorClassEnumeration(type).classCount(); if (ParameterUsageMarker.isParameterUsed(method, parameterIndex)) { // Copy the referenced classes. for (int counter = 0; counter < count; counter++) { referencedClasses[newReferencedClassIndex++] = referencedClasses[referencedClassIndex++]; } } else { // Skip the referenced classes. referencedClassIndex += count; } parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1; } // Copy the return type. { String type = internalTypeEnumeration.returnType(); int count = new DescriptorClassEnumeration(type).classCount(); for (int counter = 0; counter < count; counter++) { referencedClasses[newReferencedClassIndex++] = referencedClasses[referencedClassIndex++]; } } // Shrink the array to the proper size. if (newReferencedClassIndex == 0) { referencedClasses = null; } else if (newReferencedClassIndex < referencedClassIndex) { Clazz[] newReferencedClasses = new Clazz[newReferencedClassIndex]; System.arraycopy(referencedClasses, 0, newReferencedClasses, 0, newReferencedClassIndex); referencedClasses = newReferencedClasses; } } return referencedClasses; } }