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