MethodDescriptorShrinker.java revision b9cc48a43ed984587c939d02fba5316bf5c0df6e
1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2013 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.*; 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(" ["+programClass.getName()+"."+ 99 name+descriptor+"] -> ["+ 100 newName+newDescriptor+"]"); 101 } 102 103 ConstantPoolEditor constantPoolEditor = 104 new ConstantPoolEditor(programClass); 105 106 // Update the name, if necessary. 107 if (!newName.equals(name)) 108 { 109 programMethod.u2nameIndex = 110 constantPoolEditor.addUtf8Constant(newName); 111 } 112 113 // Update the referenced classes. 114 programMethod.referencedClasses = 115 shrinkReferencedClasses(programMethod, 116 descriptor, 117 programMethod.referencedClasses); 118 119 // Finally, update the descriptor itself. 120 programMethod.u2descriptorIndex = 121 constantPoolEditor.addUtf8Constant(newDescriptor); 122 123 // Visit the method, if required. 124 if (extraMemberVisitor != null) 125 { 126 extraMemberVisitor.visitProgramMethod(programClass, programMethod); 127 } 128 } 129 } 130 131 132 // Implementations for AttributeVisitor. 133 134 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 135 136 137 public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute) 138 { 139 // Compute the new signature. 140 String signature = clazz.getString(signatureAttribute.u2signatureIndex); 141 String newSignature = shrinkDescriptor(method, signature); 142 143 // Update the signature. 144 signatureAttribute.u2signatureIndex = 145 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature); 146 147 // Update the referenced classes. 148 signatureAttribute.referencedClasses = 149 shrinkReferencedClasses(method, 150 signature, 151 signatureAttribute.referencedClasses); 152 } 153 154 155 public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) 156 { 157 int[] annotationsCounts = parameterAnnotationsAttribute.u2parameterAnnotationsCount; 158 Annotation[][] annotations = parameterAnnotationsAttribute.parameterAnnotations; 159 160 // All parameters of non-static methods are shifted by one in the local 161 // variable frame. 162 int parameterIndex = 163 (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? 164 0 : 1; 165 166 int annotationIndex = 0; 167 int newAnnotationIndex = 0; 168 169 // Go over the parameters. 170 String descriptor = method.getDescriptor(clazz); 171 InternalTypeEnumeration internalTypeEnumeration = 172 new InternalTypeEnumeration(descriptor); 173 174 while (internalTypeEnumeration.hasMoreTypes()) 175 { 176 String type = internalTypeEnumeration.nextType(); 177 if (ParameterUsageMarker.isParameterUsed(method, parameterIndex)) 178 { 179 annotationsCounts[newAnnotationIndex] = annotationsCounts[annotationIndex]; 180 annotations[newAnnotationIndex++] = annotations[annotationIndex]; 181 } 182 183 annotationIndex++; 184 185 parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1; 186 } 187 188 // Update the number of parameters. 189 parameterAnnotationsAttribute.u2parametersCount = newAnnotationIndex; 190 191 // Clear the unused entries. 192 while (newAnnotationIndex < annotationIndex) 193 { 194 annotationsCounts[newAnnotationIndex] = 0; 195 annotations[newAnnotationIndex++] = null; 196 } 197 } 198 199 200 // Small utility methods. 201 202 /** 203 * Returns a shrunk descriptor or signature of the given method. 204 */ 205 private String shrinkDescriptor(Method method, String descriptor) 206 { 207 // All parameters of non-static methods are shifted by one in the local 208 // variable frame. 209 int parameterIndex = 210 (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? 211 0 : 1; 212 213 // Go over the parameters. 214 InternalTypeEnumeration internalTypeEnumeration = 215 new InternalTypeEnumeration(descriptor); 216 217 StringBuffer newDescriptorBuffer = new StringBuffer(); 218 219 newDescriptorBuffer.append(internalTypeEnumeration.formalTypeParameters()); 220 newDescriptorBuffer.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN); 221 222 while (internalTypeEnumeration.hasMoreTypes()) 223 { 224 String type = internalTypeEnumeration.nextType(); 225 if (ParameterUsageMarker.isParameterUsed(method, parameterIndex)) 226 { 227 newDescriptorBuffer.append(type); 228 } 229 else if (DEBUG) 230 { 231 System.out.println(" Deleting parameter #"+parameterIndex+" ["+type+"]"); 232 } 233 234 parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1; 235 } 236 237 newDescriptorBuffer.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); 238 newDescriptorBuffer.append(internalTypeEnumeration.returnType()); 239 240 return newDescriptorBuffer.toString(); 241 } 242 243 244 /** 245 * Shrinks the array of referenced classes of the given method. 246 */ 247 private Clazz[] shrinkReferencedClasses(Method method, 248 String descriptor, 249 Clazz[] referencedClasses) 250 { 251 if (referencedClasses != null) 252 { 253 // All parameters of non-static methods are shifted by one in the local 254 // variable frame. 255 int parameterIndex = 256 (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? 257 0 : 1; 258 259 int referencedClassIndex = 0; 260 int newReferencedClassIndex = 0; 261 262 // Go over the parameters. 263 InternalTypeEnumeration internalTypeEnumeration = 264 new InternalTypeEnumeration(descriptor); 265 266 // Also look at the formal type parameters. 267 String type = internalTypeEnumeration.formalTypeParameters(); 268 int count = new DescriptorClassEnumeration(type).classCount(); 269 for (int counter = 0; counter < count; counter++) 270 { 271 referencedClasses[newReferencedClassIndex++] = 272 referencedClasses[referencedClassIndex++]; 273 } 274 275 while (internalTypeEnumeration.hasMoreTypes()) 276 { 277 // Consider the classes referenced by this parameter type. 278 type = internalTypeEnumeration.nextType(); 279 count = new DescriptorClassEnumeration(type).classCount(); 280 281 if (ParameterUsageMarker.isParameterUsed(method, parameterIndex)) 282 { 283 // Copy the referenced classes. 284 for (int counter = 0; counter < count; counter++) 285 { 286 referencedClasses[newReferencedClassIndex++] = 287 referencedClasses[referencedClassIndex++]; 288 } 289 } 290 else 291 { 292 // Skip the referenced classes. 293 referencedClassIndex += count; 294 } 295 296 parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1; 297 } 298 299 // Also look at the return value. 300 type = internalTypeEnumeration.returnType(); 301 count = new DescriptorClassEnumeration(type).classCount(); 302 for (int counter = 0; counter < count; counter++) 303 { 304 referencedClasses[newReferencedClassIndex++] = 305 referencedClasses[referencedClassIndex++]; 306 } 307 308 // Clear the unused entries. 309 while (newReferencedClassIndex < referencedClassIndex) 310 { 311 referencedClasses[newReferencedClassIndex++] = null; 312 } 313 } 314 315 return referencedClasses; 316 } 317} 318