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